diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..f4418e0 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1366 @@ +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. + +[[package]] +name = "alabaster" +version = "0.7.16" +description = "A light, configurable Sphinx theme" +optional = false +python-versions = ">=3.9" +files = [ + {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, + {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, +] + +[[package]] +name = "asgiref" +version = "3.8.1" +description = "ASGI specs, helper code, and adapters" +optional = false +python-versions = ">=3.8" +files = [ + {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, + {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, +] + +[package.extras] +tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] + +[[package]] +name = "babel" +version = "2.14.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, + {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, +] + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + +[[package]] +name = "bleach" +version = "6.1.0" +description = "An easy safelist-based HTML-sanitizing tool." +optional = false +python-versions = ">=3.8" +files = [ + {file = "bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6"}, + {file = "bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe"}, +] + +[package.dependencies] +six = ">=1.9.0" +webencodings = "*" + +[package.extras] +css = ["tinycss2 (>=1.1.0,<1.3)"] + +[[package]] +name = "certifi" +version = "2024.2.2" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "cloudinary" +version = "1.39.1" +description = "Python and Django SDK for Cloudinary" +optional = false +python-versions = "*" +files = [ + {file = "cloudinary-1.39.1.tar.gz", hash = "sha256:922467259045ffdd81713b5ed2bc48f1d70f7f3e9812b8b0c206e99d665ed161"}, +] + +[package.dependencies] +certifi = "*" +six = "*" +urllib3 = ">=1.26.5" + +[package.extras] +dev = ["tox"] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "crispy-bootstrap5" +version = "2024.2" +description = "Bootstrap5 template pack for django-crispy-forms" +optional = false +python-versions = ">=3.8" +files = [ + {file = "crispy-bootstrap5-2024.2.tar.gz", hash = "sha256:7d1fa40c6faf472e30e85c72551a3d2c9eedbf0abfff920683315e4e6f670f2b"}, + {file = "crispy_bootstrap5-2024.2-py3-none-any.whl", hash = "sha256:3867e320920a6ef156e94f9e0f06a80344c453e1b3bd96cd9dc0522ae9e9afb8"}, +] + +[package.dependencies] +django = ">=4.2" +django-crispy-forms = ">=2" + +[package.extras] +test = ["pytest", "pytest-django"] + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] + +[[package]] +name = "diff-match-patch" +version = "20230430" +description = "Diff Match and Patch" +optional = false +python-versions = ">=3.7" +files = [ + {file = "diff-match-patch-20230430.tar.gz", hash = "sha256:953019cdb9c9d2c9e47b5b12bcff3cf4746fc4598eb406076fa1fc27e6a1f15c"}, + {file = "diff_match_patch-20230430-py3-none-any.whl", hash = "sha256:dce43505fb7b1b317de7195579388df0746d90db07015ed47a85e5e44930ef93"}, +] + +[package.extras] +dev = ["attribution (==1.6.2)", "black (==23.3.0)", "flit (==3.8.0)", "mypy (==1.2.0)", "ufmt (==2.1.0)", "usort (==1.0.6)"] + +[[package]] +name = "django" +version = "5.0.4" +description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." +optional = false +python-versions = ">=3.10" +files = [ + {file = "Django-5.0.4-py3-none-any.whl", hash = "sha256:916423499d75d62da7aa038d19aef23d23498d8df229775eb0a6309ee1013775"}, + {file = "Django-5.0.4.tar.gz", hash = "sha256:4bd01a8c830bb77a8a3b0e7d8b25b887e536ad17a81ba2dce5476135c73312bd"}, +] + +[package.dependencies] +asgiref = ">=3.7.0,<4" +sqlparse = ">=0.3.1" +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "django-appconf" +version = "1.0.6" +description = "A helper class for handling configuration defaults of packaged apps gracefully." +optional = false +python-versions = ">=3.7" +files = [ + {file = "django-appconf-1.0.6.tar.gz", hash = "sha256:cfe87ea827c4ee04b9a70fab90b86d704cb02f2981f89da8423cb0fabf88efbf"}, + {file = "django_appconf-1.0.6-py3-none-any.whl", hash = "sha256:c3ae442fba1ff7ec830412c5184b17169a7a1e71cf0864a4c3f93cf4c98a1993"}, +] + +[package.dependencies] +django = "*" + +[[package]] +name = "django-avatar" +version = "8.0.0" +description = "A Django app for handling user avatars" +optional = false +python-versions = "*" +files = [ + {file = "django-avatar-8.0.0.tar.gz", hash = "sha256:9e5fdea5c8b5749abc1e4d09ed2bba9ff43d8654b24fa277303097f808f027c3"}, + {file = "django_avatar-8.0.0-py3-none-any.whl", hash = "sha256:67702e80d348c2b7da09caef183abe92bba60204a384cfc2e88fbe6e9373eea8"}, +] + +[package.dependencies] +django-appconf = ">=1.0.5" +dnspython = ">=2.3.0" +Pillow = ">=10.0.1" + +[[package]] +name = "django-cloudinary-storage" +version = "0.3.0" +description = "Django package that provides Cloudinary storages for both media and static files as well as management commands for removing unnecessary files." +optional = false +python-versions = "*" +files = [ + {file = "django-cloudinary-storage-0.3.0.tar.gz", hash = "sha256:debd403a5f225c9d35c4af508ff4abc7fdc262129464134b60b2feeb2c44d3e1"}, + {file = "django_cloudinary_storage-0.3.0-py3-none-any.whl", hash = "sha256:95c855c7334141b2a485ea7eb5716de87bb6ce0d848e205debb3a3ab7bbbd49a"}, +] + +[package.dependencies] +cloudinary = ">=1.4.0" +requests = ">=2.10.0" + +[package.extras] +video = ["python-magic (>=0.4.12)"] + +[[package]] +name = "django-countries" +version = "7.6.1" +description = "Provides a country field for Django models." +optional = false +python-versions = "*" +files = [ + {file = "django-countries-7.6.1.tar.gz", hash = "sha256:c772d4e3e54afcc5f97a018544e96f246c6d9f1db51898ab0c15cd57e19437cf"}, + {file = "django_countries-7.6.1-py3-none-any.whl", hash = "sha256:1ed20842fe0f6194f91faca21076649513846a8787c9eb5aeec3cbe1656b8acc"}, +] + +[package.dependencies] +asgiref = "*" +typing-extensions = "*" + +[package.extras] +dev = ["black", "django", "djangorestframework", "graphene-django", "pytest", "pytest-django", "tox (==4.*)"] +maintainer = ["django", "zest.releaser[recommended]"] +pyuca = ["pyuca"] +test = ["djangorestframework", "graphene-django", "pytest", "pytest-cov", "pytest-django"] + +[[package]] +name = "django-crispy-forms" +version = "2.1" +description = "Best way to have Django DRY forms" +optional = false +python-versions = ">=3.8" +files = [ + {file = "django-crispy-forms-2.1.tar.gz", hash = "sha256:4d7ec431933ad4d4b5c5a6de4a584d24613c347db9ac168723c9aaf63af4bb96"}, + {file = "django_crispy_forms-2.1-py3-none-any.whl", hash = "sha256:d592044771412ae1bd539cc377203aa61d4eebe77fcbc07fbc8f12d3746d4f6b"}, +] + +[package.dependencies] +django = ">=4.2" + +[[package]] +name = "django-embed-video" +version = "1.4.9" +description = "Django app for easy embedding YouTube and Vimeo videos and music from SoundCloud." +optional = false +python-versions = "*" +files = [ + {file = "django-embed-video-1.4.9.tar.gz", hash = "sha256:c50c76f2c3b7c464efb281a47d579689dca4f681b8efefec1c8ae29d20d6df99"}, + {file = "django_embed_video-1.4.9-py3-none-any.whl", hash = "sha256:a82097e160039796c9da08cef16b1aef23b319844839131d681174d019429fa5"}, +] + +[package.dependencies] +Django = ">=3.2" +requests = ">=2.19" + +[[package]] +name = "django-etc" +version = "1.4.0" +description = "Tiny stuff for Django that won't fit into separate apps." +optional = false +python-versions = "*" +files = [ + {file = "django-etc-1.4.0.tar.gz", hash = "sha256:fa7dbcdba5d0dd3b42eac54463fe24a9e1202a1492ecbf50ed0bc1555074af8f"}, + {file = "django_etc-1.4.0-py3-none-any.whl", hash = "sha256:61aee75a97ef868830260de86f2a69896992776c325624c46827c831101cbd9f"}, +] + +[[package]] +name = "django-extensions" +version = "3.2.3" +description = "Extensions for Django" +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"}, + {file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"}, +] + +[package.dependencies] +Django = ">=3.2" + +[[package]] +name = "django-gamma-cloudinary" +version = "0.2.3" +description = "A Django storage backend to integrate with Cloudinary for static files and media storage." +optional = false +python-versions = "*" +files = [ + {file = "django-gamma-cloudinary-0.2.3.tar.gz", hash = "sha256:1b2cd885d31861d6a8521e59bcef1c0e2b0071bfd9a86ea2acced8e48f8e16c1"}, + {file = "django_gamma_cloudinary-0.2.3-py3-none-any.whl", hash = "sha256:99526139e7b7ba5187a006b0afd42f67ae5062b5507a5f407d980adbf0741972"}, +] + +[package.dependencies] +cloudinary = ">=1.24.0" +Django = ">=3.1" +python-magic = ">=0.4.22" +requests = ">=2.10.0" + +[[package]] +name = "django-grappelli" +version = "3.0.8" +description = "A jazzy skin for the Django Admin-Interface." +optional = false +python-versions = "*" +files = [ + {file = "django-grappelli-3.0.8.tar.gz", hash = "sha256:0c24a0128a8e94b0f251a99d71b96738e4cf546d20fd993d03b26c05c8642fdd"}, + {file = "django_grappelli-3.0.8-py2.py3-none-any.whl", hash = "sha256:4664f14cd8f296e01130d167cb869a106644818bf84953dfeabff498be937319"}, +] + +[[package]] +name = "django-hashid-field" +version = "3.4.0" +description = "A Hashids obfuscated Django Model Field" +optional = false +python-versions = "*" +files = [ + {file = "django-hashid-field-3.4.0.tar.gz", hash = "sha256:d8981e4506bd6d75bf1f9cc77360770991d77b19a2ef920a878ac85092e84810"}, + {file = "django_hashid_field-3.4.0-py3-none-any.whl", hash = "sha256:9e06fd3b90274cb37750b1e9538ceab23d8de87a9b600530026d295fb0e23569"}, +] + +[package.dependencies] +Django = ">=1.11" +hashids = ">=1.2.0" + +[[package]] +name = "django-hitcount" +version = "1.3.5" +description = "Hit counting application for Django." +optional = false +python-versions = "*" +files = [ + {file = "django-hitcount-1.3.5.tar.gz", hash = "sha256:5fe70720a75e49bfcee5c4312a7df989bb9e22848039bba1de4406f96fb9cf4d"}, + {file = "django_hitcount-1.3.5-py2.py3-none-any.whl", hash = "sha256:b04debff8fd7fe2d255e1d357c27f838aa45c54ed1835b3fd01af30df5319756"}, +] + +[package.dependencies] +django-etc = ">=1.2.0" + +[[package]] +name = "django-imagekit" +version = "5.0.0" +description = "Automated image processing for Django models." +optional = false +python-versions = "*" +files = [ + {file = "django-imagekit-5.0.0.tar.gz", hash = "sha256:aae9f74a8e9b6ceb5d15f7d8e266302901e76d9f532c78bd5135cb0fa206a6b0"}, + {file = "django_imagekit-5.0.0-py3-none-any.whl", hash = "sha256:a8e77ed6549751026a51f961bb2cd5fda739be691496da8eecbe68ffb966c261"}, +] + +[package.dependencies] +django-appconf = "*" +pilkit = "*" + +[package.extras] +async = ["django-celery (>=3.0)"] +async-dramatiq = ["django-dramatiq (>=0.4.0)"] +async-rq = ["django-rq (>=0.6.0)"] + +[[package]] +name = "django-import-export" +version = "3.3.8" +description = "Django application and library for importing and exporting data with included admin integration." +optional = false +python-versions = ">=3.8" +files = [ + {file = "django-import-export-3.3.8.tar.gz", hash = "sha256:4deabc557801d368093608c86fd0f4831bc9540e2ea41ca2f023e2efb3eb6f48"}, + {file = "django_import_export-3.3.8-py3-none-any.whl", hash = "sha256:2eac09e8cec8670f36e24314760448011ad23c51e8fb930d55f50d0c3c926da0"}, +] + +[package.dependencies] +diff-match-patch = "*" +Django = ">=3.2" +tablib = {version = "3.5.0", extras = ["html", "ods", "xls", "xlsx", "yaml"]} + +[[package]] +name = "django-markdownx" +version = "4.0.7" +description = "A comprehensive Markdown editor built for Django." +optional = false +python-versions = "*" +files = [ + {file = "django-markdownx-4.0.7.tar.gz", hash = "sha256:38aa331c2ca0bee218b77f462361b5393e4727962bc6021939c09048363cb6ea"}, + {file = "django_markdownx-4.0.7-py2.py3-none-any.whl", hash = "sha256:c1975ae3053481d4c111abd38997a5b5bb89235a1e3215f995d835942925fe7b"}, +] + +[package.dependencies] +Django = "*" +Markdown = "*" +Pillow = "*" + +[[package]] +name = "django-next-prev" +version = "1.1.0" +description = "Django utility to retrieve the next or previous object, given a current object and a queryset." +optional = false +python-versions = "*" +files = [ + {file = "django-next-prev-1.1.0.tar.gz", hash = "sha256:eb79dcde18bb0c75c809224ed8656417fb3e56eb7e7064552772ffb11d203885"}, +] + +[package.dependencies] +Django = ">=1.8" + +[[package]] +name = "django-recaptcha" +version = "4.0.0" +description = "Django recaptcha form field/widget app." +optional = false +python-versions = "*" +files = [ + {file = "django-recaptcha-4.0.0.tar.gz", hash = "sha256:5316438f97700c431d65351470d1255047e3f2cd9af0f2f13592b637dad9213e"}, + {file = "django_recaptcha-4.0.0-py3-none-any.whl", hash = "sha256:0d912d5c7c009df4e47accd25029133d47a74342dbd2a8edc2877b6bffa971a3"}, +] + +[package.dependencies] +django = "*" + +[[package]] +name = "django-robohash-svg" +version = "0.9.5" +description = "Library for creating svg robots" +optional = false +python-versions = "*" +files = [ + {file = "django-robohash-svg-0.9.5.tar.gz", hash = "sha256:7a14e828163614ba033cf64ba4754d24e5fc87f8b68ccb1382c49cd1c50e37a6"}, +] + +[package.dependencies] +django = ">=2" +Sphinx = "*" + +[[package]] +name = "django-simple-history" +version = "3.5.0" +description = "Store model history and view/revert changes from admin site." +optional = false +python-versions = ">=3.8" +files = [ + {file = "django-simple-history-3.5.0.tar.gz", hash = "sha256:eef2943d7c846270f11c8136f38a3ba00de2bebd7b1b89037de5d9b148ed677a"}, + {file = "django_simple_history-3.5.0-py3-none-any.whl", hash = "sha256:0030b3bb5ed219d6453320213e75cfa00dab7a381946124df9411729101c8874"}, +] + +[package.dependencies] +asgiref = ">=3.6" + +[[package]] +name = "django-slugify-processor" +version = "1.6.0" +description = "pipeline for slugification edgecases in django" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "django_slugify_processor-1.6.0-py3-none-any.whl", hash = "sha256:5b961c80a27ca957aaf96b259084562ca92e76216c291469946592f256a1d731"}, + {file = "django_slugify_processor-1.6.0.tar.gz", hash = "sha256:8ece75fda3906d43b23610edb32d4f50c92490cc4cb7bca83bf73d1330a5c285"}, +] + +[package.dependencies] +Django = ">=3.2" +django-extensions = "*" + +[[package]] +name = "django-summernote" +version = "0.8.20.0" +description = "Summernote plugin for Django" +optional = false +python-versions = "*" +files = [ + {file = "django-summernote-0.8.20.0.tar.gz", hash = "sha256:52e9b12438ed9eac0d77729f758f2aae06e468b5cbce133e24100d58ae4e43a8"}, +] + +[package.dependencies] +bleach = "*" +django = "*" + +[package.extras] +dev = ["django-dummy-plug", "pytest", "pytest-django"] + +[[package]] +name = "django-taggit" +version = "5.0.1" +description = "django-taggit is a reusable Django application for simple tagging." +optional = false +python-versions = ">=3.8" +files = [ + {file = "django-taggit-5.0.1.tar.gz", hash = "sha256:edcd7db1e0f35c304e082a2f631ddac2e16ef5296029524eb792af7430cab4cc"}, + {file = "django_taggit-5.0.1-py3-none-any.whl", hash = "sha256:a0ca8a28b03c4b26c2630fd762cb76ec39b5e41abf727a7b66f897a625c5e647"}, +] + +[package.dependencies] +Django = ">=4.1" + +[[package]] +name = "django-thumbs-v2" +version = "0.4.1" +description = "The easiest way to create thumbnails for your images with Django. Works with any storage backend." +optional = false +python-versions = "*" +files = [ + {file = "django-thumbs-v2-0.4.1.tar.gz", hash = "sha256:c5395c2b66b7db2794c4e87dee7a0c540663775eeaa85f6f3712d57a4efbf603"}, + {file = "django_thumbs_v2-0.4.1-py2.py3-none-any.whl", hash = "sha256:8dd8c9a0fa29c75f7745a0a85e304ebac98cf93d3daa510d742d9fcd88f62394"}, + {file = "django_thumbs_v2-0.4.1-py3-none-any.whl", hash = "sha256:9a36e1214bef67b76f916964c5bcc92961faa8b42cb2756327c2b697c754cbae"}, +] + +[[package]] +name = "django-tinymce" +version = "4.0.0" +description = "A Django application that contains a widget to render a" +optional = false +python-versions = ">=3.8" +files = [ + {file = "django-tinymce-4.0.0.tar.gz", hash = "sha256:13a63c81c5bb3683d135f5b1ec4c7da68bc880cf5138ffb266b608914ba12236"}, + {file = "django_tinymce-4.0.0-py3-none-any.whl", hash = "sha256:2b495dca8a6d36831ffd256e930e0919361468862eb7e5ff26f9631bc42061e0"}, +] + +[package.dependencies] +django = ">=3.2" + +[[package]] +name = "djangorestframework" +version = "3.15.1" +description = "Web APIs for Django, made easy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "djangorestframework-3.15.1-py3-none-any.whl", hash = "sha256:3ccc0475bce968608cf30d07fb17d8e52d1d7fc8bfe779c905463200750cbca6"}, + {file = "djangorestframework-3.15.1.tar.gz", hash = "sha256:f88fad74183dfc7144b2756d0d2ac716ea5b4c7c9840995ac3bfd8ec034333c1"}, +] + +[package.dependencies] +django = ">=3.0" + +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "docutils" +version = "0.20.1" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, +] + +[[package]] +name = "et-xmlfile" +version = "1.1.0" +description = "An implementation of lxml.xmlfile for the standard library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"}, + {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"}, +] + +[[package]] +name = "hashids" +version = "1.3.1" +description = "Implements the hashids algorithm in python. For more information, visit http://hashids.org/" +optional = false +python-versions = ">=2.7" +files = [ + {file = "hashids-1.3.1-py2.py3-none-any.whl", hash = "sha256:8bddd1acba501bfc9306e7e5a99a1667f4f2cacdc20cbd70bcc5ddfa5147c94c"}, + {file = "hashids-1.3.1.tar.gz", hash = "sha256:6c3dc775e65efc2ce2c157a65acb776d634cb814598f406469abef00ae3f635c"}, +] + +[package.extras] +test = ["pytest (>=2.1.0)"] + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "jinja2" +version = "3.1.3" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, + {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markdown" +version = "3.6" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.6-py3-none-any.whl", hash = "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f"}, + {file = "Markdown-3.6.tar.gz", hash = "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"}, +] + +[package.extras] +docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markuppy" +version = "1.14" +description = "An HTML/XML generator" +optional = false +python-versions = "*" +files = [ + {file = "MarkupPy-1.14.tar.gz", hash = "sha256:1adee2c0a542af378fe84548ff6f6b0168f3cb7f426b46961038a2bcfaad0d5f"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "odfpy" +version = "1.4.1" +description = "Python API and tools to manipulate OpenDocument files" +optional = false +python-versions = "*" +files = [ + {file = "odfpy-1.4.1.tar.gz", hash = "sha256:db766a6e59c5103212f3cc92ec8dd50a0f3a02790233ed0b52148b70d3c438ec"}, +] + +[package.dependencies] +defusedxml = "*" + +[[package]] +name = "openpyxl" +version = "3.1.2" +description = "A Python library to read/write Excel 2010 xlsx/xlsm files" +optional = false +python-versions = ">=3.6" +files = [ + {file = "openpyxl-3.1.2-py2.py3-none-any.whl", hash = "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5"}, + {file = "openpyxl-3.1.2.tar.gz", hash = "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184"}, +] + +[package.dependencies] +et-xmlfile = "*" + +[[package]] +name = "packaging" +version = "24.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, +] + +[[package]] +name = "pilkit" +version = "3.0" +description = "A collection of utilities and processors for the Python Imaging Library." +optional = false +python-versions = "*" +files = [ + {file = "pilkit-3.0-py3-none-any.whl", hash = "sha256:fe1707b0411a1d0cbf9ad3986779fa5a346cec4582a188740924aa39f504d117"}, + {file = "pilkit-3.0.tar.gz", hash = "sha256:f6719e8cc0482e5447f5cb94f18b949d8e604ea9673a9b019c74d41b779e4eab"}, +] + +[package.dependencies] +Pillow = ">=7.0" + +[[package]] +name = "pillow" +version = "10.3.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "python-magic" +version = "0.4.27" +description = "File type identification using libmagic" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b"}, + {file = "python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3"}, +] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "requests" +version = "2.31.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sorl-thumbnail" +version = "12.10.0" +description = "Thumbnails for Django" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sorl-thumbnail-12.10.0.tar.gz", hash = "sha256:de95a49217fdfeced222fa3ceaa01d312ee2f8aad56ba34d6c70f2dee9a84938"}, + {file = "sorl_thumbnail-12.10.0-py3-none-any.whl", hash = "sha256:733eb2eee392d4a874f88fb3ed6f0572fa9c361b06e0411b83e435ba69c51f52"}, +] + +[[package]] +name = "sphinx" +version = "7.2.6" +description = "Python documentation generator" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinx-7.2.6-py3-none-any.whl", hash = "sha256:1e09160a40b956dc623c910118fa636da93bd3ca0b9876a7b3df90f07d691560"}, + {file = "sphinx-7.2.6.tar.gz", hash = "sha256:9a5160e1ea90688d5963ba09a2dcd8bdd526620edbb65c328728f1b2228d5ab5"}, +] + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.18.1,<0.21" +imagesize = ">=1.3" +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.14" +requests = ">=2.25.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.9" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] +test = ["cython (>=3.0)", "filelock", "html5lib", "pytest (>=4.6)", "setuptools (>=67.0)"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.8" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, + {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.6" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, + {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.5" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, + {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.7" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, + {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.10" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, + {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sqlparse" +version = "0.4.4" +description = "A non-validating SQL parser." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, + {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, +] + +[package.extras] +dev = ["build", "flake8"] +doc = ["sphinx"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "tablib" +version = "3.5.0" +description = "Format agnostic tabular data library (XLS, JSON, YAML, CSV, etc.)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tablib-3.5.0-py3-none-any.whl", hash = "sha256:9821caa9eca6062ff7299fa645e737aecff982e6b2b42046928a6413c8dabfd9"}, + {file = "tablib-3.5.0.tar.gz", hash = "sha256:f6661dfc45e1d4f51fa8a6239f9c8349380859a5bfaa73280645f046d6c96e33"}, +] + +[package.dependencies] +markuppy = {version = "*", optional = true, markers = "extra == \"html\""} +odfpy = {version = "*", optional = true, markers = "extra == \"ods\""} +openpyxl = {version = ">=2.6.0", optional = true, markers = "extra == \"xlsx\""} +pyyaml = {version = "*", optional = true, markers = "extra == \"yaml\""} +xlrd = {version = "*", optional = true, markers = "extra == \"xls\""} +xlwt = {version = "*", optional = true, markers = "extra == \"xls\""} + +[package.extras] +all = ["markuppy", "odfpy", "openpyxl (>=2.6.0)", "pandas", "pyyaml", "tabulate", "xlrd", "xlwt"] +cli = ["tabulate"] +html = ["markuppy"] +ods = ["odfpy"] +pandas = ["pandas"] +xls = ["xlrd", "xlwt"] +xlsx = ["openpyxl (>=2.6.0)"] +yaml = ["pyyaml"] + +[[package]] +name = "typing-extensions" +version = "4.11.0" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + +[[package]] +name = "urllib3" +version = "2.2.1" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, + {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +optional = false +python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[package]] +name = "xlrd" +version = "2.0.1" +description = "Library for developers to extract data from Microsoft Excel (tm) .xls spreadsheet files" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "xlrd-2.0.1-py2.py3-none-any.whl", hash = "sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd"}, + {file = "xlrd-2.0.1.tar.gz", hash = "sha256:f72f148f54442c6b056bf931dbc34f986fd0c3b0b6b5a58d013c9aef274d0c88"}, +] + +[package.extras] +build = ["twine", "wheel"] +docs = ["sphinx"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "xlwt" +version = "1.3.0" +description = "Library to create spreadsheet files compatible with MS Excel 97/2000/XP/2003 XLS files, on any platform, with Python 2.6, 2.7, 3.3+" +optional = false +python-versions = "*" +files = [ + {file = "xlwt-1.3.0-py2.py3-none-any.whl", hash = "sha256:a082260524678ba48a297d922cc385f58278b8aa68741596a87de01a9c628b2e"}, + {file = "xlwt-1.3.0.tar.gz", hash = "sha256:c59912717a9b28f1a3c2a98fd60741014b06b043936dcecbc113eaaada156c88"}, +] + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "516169c7ab8472b943f79f1c252c988e9383f17146abfe54eff739636423d496" diff --git a/pycons-site/__init__.py b/pycons-site/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/about/__init__.py b/pycons-site/about/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/about/admin.py b/pycons-site/about/admin.py new file mode 100644 index 0000000..0be6eb8 --- /dev/null +++ b/pycons-site/about/admin.py @@ -0,0 +1,103 @@ +# Core Django imports. +from django.contrib import admin + +# About application imports. +from .models import ( + About, + Venue, + Travel_Advice, + IOCGroup, + IOCMember, + LOCGroup, + LOCMember, + VolunteerGroup, + Volunteer, +) + +# If you plan to use adminsortable2 (optional) +# from adminsortable2.admin import SortableAdminMixin + +# AboutAdmin +class AboutAdmin(admin.ModelAdmin): + list_display = ('about_title', 'user', 'event_year', 'date_created') + ordering = ['-date_created'] + exclude = ('user',) # Exclude the user field from the admin form + + def save_model(self, request, obj, form, change): + if not obj.pk: # If the object is being created (and not modified) + obj.user = request.user # Set the user to the current user + super().save_model(request, obj, form, change) + +# VenueAdmin +class VenueAdmin(admin.ModelAdmin): + list_display = ('name', 'event_year', 'date_created') + ordering = ['-date_created'] + exclude = ('user',) + + def save_model(self, request, obj, form, change): + if not obj.pk: + obj.user = request.user + super().save_model(request, obj, form, change) + +# TravelAdviceAdmin +class TravelAdviceAdmin(admin.ModelAdmin): + list_display = ('title', 'user', 'event_year', 'date_created') + ordering = ['-date_created'] + exclude = ('user',) + + def save_model(self, request, obj, form, change): + if not obj.pk: + obj.user = request.user + super().save_model(request, obj, form, change) + +# IOCGroupAdmin +class IOCGroupAdmin(admin.ModelAdmin): + list_display = ('name', 'event_year') + list_filter = ('event_year',) + search_fields = ('name',) + +# IOCMemberAdmin +@admin.register(IOCMember) +class IOCMemberAdmin(admin.ModelAdmin): + list_display = ('name', 'event_year', 'is_lead') + list_filter = ('event_year', 'is_lead') + search_fields = ('name',) + list_editable = ('is_lead',) + filter_horizontal = ('groups',) # To make many-to-many field manageable + +# LOCGroupAdmin +class LOCGroupAdmin(admin.ModelAdmin): + list_display = ('name', 'event_year') + list_filter = ('event_year',) + search_fields = ('name',) + +# LOCMemberAdmin +@admin.register(LOCMember) +class LOCMemberAdmin(admin.ModelAdmin): + list_display = ('name', 'event_year', 'is_lead') + list_filter = ('event_year', 'is_lead') + search_fields = ('name',) + list_editable = ('is_lead',) + filter_horizontal = ('groups',) # To make many-to-many field manageable + +# VolunteerGroupAdmin +class VolunteerGroupAdmin(admin.ModelAdmin): + list_display = ('name', 'event_year') + list_filter = ('event_year',) + search_fields = ('name',) + +# VolunteerAdmin +class VolunteerAdmin(admin.ModelAdmin): + list_display = ('name', 'event_year') + list_filter = ('event_year', 'groups') + search_fields = ('name',) + filter_horizontal = ('groups',) + +# Register your models and admin classes +admin.site.register(About, AboutAdmin) +admin.site.register(Venue, VenueAdmin) +admin.site.register(Travel_Advice, TravelAdviceAdmin) +admin.site.register(IOCGroup, IOCGroupAdmin) +admin.site.register(LOCGroup, LOCGroupAdmin) +admin.site.register(VolunteerGroup, VolunteerGroupAdmin) +admin.site.register(Volunteer, VolunteerAdmin) diff --git a/pycons-site/about/apps.py b/pycons-site/about/apps.py new file mode 100644 index 0000000..d6ed2ac --- /dev/null +++ b/pycons-site/about/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AboutConfig(AppConfig): + name = 'about' diff --git a/pycons-site/about/forms.py b/pycons-site/about/forms.py new file mode 100644 index 0000000..8a2ffbb --- /dev/null +++ b/pycons-site/about/forms.py @@ -0,0 +1,40 @@ +""" +Forms and validation code for user registration. + +Note that all of these forms assume Django's bundle default ``User`` +model; since it's not possible for a form to anticipate in advance the +needs of custom user models, you will need to write your own forms if +you're using a custom model. + +""" +from django import forms + +from registration.users import UserModel + +# Third Parties +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Submit +from django_recaptcha.fields import ReCaptchaField + + +User = UserModel() + + +from .models import About + +class AboutForm(forms.ModelForm): + captcha = ReCaptchaField() + + class Meta: + model = About + fields = ('about_title', 'about_image_one', 'section_one_title', 'section_one', 'section_two_title', 'section_two', 'section_three_title', 'section_three', 'user',) + + def __init__(self, *args, **kwargs): + super(AboutForm, self).__init__(*args, **kwargs) + self.fields['user'].disabled = True + + self.helper = FormHelper() + self.helper.form_id = 'id-Crispy_AboutForm' + self.helper.form_class = 'form-horizontal' + self.helper.add_input(Submit('update', 'About ')) + diff --git a/pycons-site/about/migrations/0001_initial.py b/pycons-site/about/migrations/0001_initial.py new file mode 100644 index 0000000..28fa5fe --- /dev/null +++ b/pycons-site/about/migrations/0001_initial.py @@ -0,0 +1,70 @@ +# Generated by Django 3.2 on 2022-08-17 12:05 + +from django.conf import settings +import django.contrib.auth.models +from django.db import migrations, models +import django.db.models.deletion +import embed_video.fields +import markdownx.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Venue', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Venue of PyCon Africa', max_length=250)), + ('content', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - Content.')), + ('content_two', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - Content side two.')), + ('link_to_preview_video_url', embed_video.fields.EmbedVideoField(blank=True, default='', help_text='Link to Preview video on your Youtube or Google drive')), + ('google_map', models.CharField(default='', help_text='Venue Google map ID', max_length=500)), + ('location', models.CharField(default='', help_text='Location of Venue', max_length=250)), + ('location_address', models.CharField(default='', help_text='Location of Venue', max_length=250)), + ('location_website', models.URLField(blank=True, default='', help_text="Venue's website if any")), + ('image_one', models.URLField(default='', help_text='Link to image')), + ('image_two', models.URLField(default='', help_text='Link to image')), + ('first_day_of_event', models.DateTimeField(blank=True, null=True)), + ('last_day_of_event', models.DateTimeField(blank=True, null=True)), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'venue', + 'verbose_name_plural': 'venues', + 'unique_together': {('name',)}, + }, + ), + migrations.CreateModel( + name='Travel_Advice', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Travel Advice PyCon Africa', max_length=250)), + ('advice', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - Travel Advice PyCon Africa.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(default=django.contrib.auth.models.User, on_delete=django.db.models.deletion.CASCADE, related_name='travel_advice', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='About', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('about_title', models.CharField(help_text='About PyCon Africa', max_length=250)), + ('about_image_one', models.ImageField(help_text='Upload your cover image or leave blank to use our default image', upload_to='about_page')), + ('about_image_two', models.URLField(default='', help_text='Link to image')), + ('section_one', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - About PyCon Africa.')), + ('section_two', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - About PyCon Africa.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='about_us', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/pycons-site/about/migrations/0002_team.py b/pycons-site/about/migrations/0002_team.py new file mode 100644 index 0000000..10e1f64 --- /dev/null +++ b/pycons-site/about/migrations/0002_team.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2 on 2022-11-15 15:09 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import markdownx.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('about', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Team', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('team_title', models.CharField(blank=True, default='Team', help_text='Teams PyCon Africa', max_length=250)), + ('team_image_one', models.ImageField(help_text='Upload your cover image or leave blank to use our default image', upload_to='team_page')), + ('team_image_two', models.URLField(default='', help_text='Link to image')), + ('section_one', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - PyCon Africa Team.')), + ('session_title', models.CharField(blank=True, default='Volunteers', help_text='Session Title eg. Volunteers, Team, Mentor', max_length=250)), + ('section_two', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - PyCon Africa Volunteers.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teams', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/pycons-site/about/migrations/0003_alter_team_user.py b/pycons-site/about/migrations/0003_alter_team_user.py new file mode 100644 index 0000000..456847c --- /dev/null +++ b/pycons-site/about/migrations/0003_alter_team_user.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2 on 2023-04-11 16:39 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('about', '0002_team'), + ] + + operations = [ + migrations.AlterField( + model_name='team', + name='user', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teams_user', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/pycons-site/about/migrations/0004_alter_about_id_alter_team_id_alter_travel_advice_id_and_more.py b/pycons-site/about/migrations/0004_alter_about_id_alter_team_id_alter_travel_advice_id_and_more.py new file mode 100644 index 0000000..0720bd2 --- /dev/null +++ b/pycons-site/about/migrations/0004_alter_about_id_alter_team_id_alter_travel_advice_id_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.0.2 on 2024-02-29 23:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('about', '0003_alter_team_user'), + ] + + operations = [ + migrations.AlterField( + model_name='about', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='team', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='travel_advice', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + migrations.AlterField( + model_name='venue', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/pycons-site/about/migrations/0005_about_event_year_team_event_year_and_more.py b/pycons-site/about/migrations/0005_about_event_year_team_event_year_and_more.py new file mode 100644 index 0000000..700f0c5 --- /dev/null +++ b/pycons-site/about/migrations/0005_about_event_year_team_event_year_and_more.py @@ -0,0 +1,35 @@ +# Generated by Django 5.0.2 on 2024-03-02 16:16 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('about', '0004_alter_about_id_alter_team_id_alter_travel_advice_id_and_more'), + ('home', '0008_pyconevent'), + ] + + operations = [ + migrations.AddField( + model_name='about', + name='event_year', + field=models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='abouts', to='home.eventyear'), + ), + migrations.AddField( + model_name='team', + name='event_year', + field=models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='teams', to='home.eventyear'), + ), + migrations.AddField( + model_name='travel_advice', + name='event_year', + field=models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='travel_advices', to='home.eventyear'), + ), + migrations.AddField( + model_name='venue', + name='event_year', + field=models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='venues', to='home.eventyear'), + ), + ] diff --git a/pycons-site/about/migrations/0006_remove_about_about_image_two_about_about_tagline_and_more.py b/pycons-site/about/migrations/0006_remove_about_about_image_two_about_about_tagline_and_more.py new file mode 100644 index 0000000..19a6a70 --- /dev/null +++ b/pycons-site/about/migrations/0006_remove_about_about_image_two_about_about_tagline_and_more.py @@ -0,0 +1,43 @@ +# Generated by Django 5.0.2 on 2024-03-02 18:18 + +import markdownx.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('about', '0005_about_event_year_team_event_year_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='about', + name='about_image_two', + ), + migrations.AddField( + model_name='about', + name='about_tagline', + field=models.CharField(blank=True, help_text='Tagline for the year', max_length=250), + ), + migrations.AddField( + model_name='about', + name='section_one_title', + field=models.CharField(blank=True, default='WHAT IS | PYCON AFRICA?', help_text='About PyCon Africa', max_length=250), + ), + migrations.AddField( + model_name='about', + name='section_three', + field=markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - About PyCon Africa.'), + ), + migrations.AddField( + model_name='about', + name='section_three_title', + field=models.CharField(blank=True, default='THE | TEAM', help_text='About PyCon Africa', max_length=250), + ), + migrations.AddField( + model_name='about', + name='section_two_title', + field=models.CharField(blank=True, default='THE | PROGRAM', help_text='About PyCon Africa', max_length=250), + ), + ] diff --git a/pycons-site/about/migrations/0007_iocgroup_iocmember_volunteergroup_volunteer_and_more.py b/pycons-site/about/migrations/0007_iocgroup_iocmember_volunteergroup_volunteer_and_more.py new file mode 100644 index 0000000..4ce4d4c --- /dev/null +++ b/pycons-site/about/migrations/0007_iocgroup_iocmember_volunteergroup_volunteer_and_more.py @@ -0,0 +1,52 @@ +# Generated by Django 5.0.2 on 2024-03-05 11:59 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('about', '0006_remove_about_about_image_two_about_about_tagline_and_more'), + ('home', '0008_pyconevent'), + ] + + operations = [ + migrations.CreateModel( + name='IOCGroup', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('event_year', models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='ioc_groups', to='home.eventyear')), + ], + ), + migrations.CreateModel( + name='IOCMember', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('event_year', models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='ioc_members', to='home.eventyear')), + ('groups', models.ManyToManyField(related_name='iocs', to='about.iocgroup')), + ], + ), + migrations.CreateModel( + name='VolunteerGroup', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('event_year', models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='volunteer_groups', to='home.eventyear')), + ], + ), + migrations.CreateModel( + name='Volunteer', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('event_year', models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='volunteers', to='home.eventyear')), + ('groups', models.ManyToManyField(related_name='volunteers', to='about.volunteergroup')), + ], + ), + migrations.DeleteModel( + name='Team', + ), + ] diff --git a/pycons-site/about/migrations/0008_iocmember_country.py b/pycons-site/about/migrations/0008_iocmember_country.py new file mode 100644 index 0000000..d5367fa --- /dev/null +++ b/pycons-site/about/migrations/0008_iocmember_country.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0.2 on 2024-03-05 12:43 + +import django_countries.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('about', '0007_iocgroup_iocmember_volunteergroup_volunteer_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='iocmember', + name='country', + field=django_countries.fields.CountryField(default='GH', max_length=2), + ), + ] diff --git a/pycons-site/about/migrations/0009_about_section_four.py b/pycons-site/about/migrations/0009_about_section_four.py new file mode 100644 index 0000000..2b11f12 --- /dev/null +++ b/pycons-site/about/migrations/0009_about_section_four.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0.2 on 2024-05-07 13:33 + +import markdownx.models +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('about', '0008_iocmember_country'), + ] + + operations = [ + migrations.AddField( + model_name='about', + name='section_four', + field=markdownx.models.MarkdownxField(blank=True, default='', help_text='[Supports Markdown] - More about PyCon Africa.'), + ), + ] diff --git a/pycons-site/about/migrations/__init__.py b/pycons-site/about/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/about/mixins.py b/pycons-site/about/mixins.py new file mode 100644 index 0000000..678e5f4 --- /dev/null +++ b/pycons-site/about/mixins.py @@ -0,0 +1,23 @@ +from django.core.exceptions import PermissionDenied + +class EditOwnAboutMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnAboutMixin, self).get_object(*args, **kwargs) + self.check_permission(obj) + return obj + def check_permission(self, obj): + if hasattr(obj, 'user'): + obj = obj.user + if obj == self.request.user: + return + else: + raise PermissionDenied() + +class EditOwnLoginMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnLoginMixin, self).get_object(*args, **kwargs) + if obj.email == self.request.user.email: + return obj + else: + raise PermissionDenied + diff --git a/pycons-site/about/models.py b/pycons-site/about/models.py new file mode 100644 index 0000000..0fdfa0b --- /dev/null +++ b/pycons-site/about/models.py @@ -0,0 +1,162 @@ +from django.db import models +from django.urls import reverse +from django.conf import settings +from django.contrib.auth.models import User +from markdownx.models import MarkdownxField +from django.utils import timezone +from markdownx.models import MarkdownxField +from django.utils import timezone +from embed_video.fields import EmbedVideoField +from home.models import EventYear +from django_countries.fields import CountryField + + +class About(models.Model): + about_title = models.CharField(max_length=250, null=False, blank=False, help_text='About PyCon') + about_tagline = models.CharField(max_length=250, null=False, blank=True, help_text='Tagline for the year') + about_image_one = models.ImageField(help_text="Upload your cover image or leave blank to use our default image", + upload_to='about_page') + section_one_title = models.CharField(max_length=250, null=False, blank=True, default="WHAT IS | PYCON ?", help_text='About PyCon') + section_one = MarkdownxField(default='', help_text = "[Supports Markdown] - About PyCon .", null=False, blank=False + ) + section_two_title = models.CharField(max_length=250, null=False, blank=True, default="THE | PROGRAM", help_text='About PyCon ') + section_two = MarkdownxField(default='', help_text = "[Supports Markdown] - About PyCon .", null=False, blank=False + ) + section_three_title = models.CharField(max_length=250, null=False, blank=True, default="THE | TEAM", help_text='About PyCon') + section_three = MarkdownxField(default='', help_text = "[Supports Markdown] - About PyCon.", null=False, blank=False + ) + section_four = MarkdownxField(default='', help_text = "[Supports Markdown] - More about PyCon.", null=False, blank=True + ) + user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, null=True, related_name='about_us', on_delete=models.CASCADE) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='abouts') + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.about_title + + def get_absolute_url(self): + return reverse("about_home") + + + +class Venue(models.Model): + name = models.CharField(max_length=250, null=False, blank=False, help_text='Venue of PyCon') + content = MarkdownxField(default='', help_text = "[Supports Markdown] - Content.", null=False, blank=False + ) + content_two = MarkdownxField(default='', help_text = "[Supports Markdown] - Content side two.", null=False, blank=False + ) + link_to_preview_video_url = EmbedVideoField(default="", blank=True, help_text='Link to Preview video on your Youtube or Google drive') + google_map = models.CharField(default="",max_length=500, null=False, blank=False, help_text='Venue Google map ID') + location = models.CharField(default="",max_length=250, null=False, blank=False, help_text='Location of Venue') + location_address = models.CharField(default="",max_length=250, null=False, blank=False, help_text='Location of Venue') + location_website = models.URLField(default="", blank=True, help_text="Venue's website if any") + image_one = models.URLField(default="", blank=False, help_text='Link to image') + image_two = models.URLField(default="", blank=False, help_text='Link to image') + first_day_of_event = models.DateTimeField(blank=True, null=True) + last_day_of_event = models.DateTimeField(blank=True, null=True) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='venues') + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + class Meta: + unique_together = ('name',) + verbose_name = 'venue' + verbose_name_plural = 'venues' + + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse("venue") + + +class Travel_Advice(models.Model): + title = models.CharField(max_length=250, null=False, blank=False, help_text='Travel Advice') + advice = MarkdownxField(default='', help_text = "[Supports Markdown] - Travel Advice", null=False, blank=False + ) + user = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='travel_advice',default=User) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='travel_advices') + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("travel_advice") + + +class IOCGroup(models.Model): + """ + Represents a group or category of IOC. + """ + name = models.CharField(max_length=100) # e.g., Moderating Team, Talks & Workshop Support + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name="ioc_groups") + + def __str__(self): + return self.name + +class IOCMember(models.Model): + """ + Represents a core team member. + """ + name = models.CharField(max_length=100) + link = models.CharField(default="",blank=True, max_length=100) + groups = models.ManyToManyField(IOCGroup, related_name="iocs",blank=True) + country = CountryField(default="GH",blank=False, blank_label='(select member contributing country)') + country_2 = models.CharField(max_length=50, default="",blank=True, help_text='(enter member"s 2nd contributing country if any)') + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='ioc_members') + is_lead = models.BooleanField(default=False, help_text='Indicate if this member is a lead') + + def __str__(self): + return self.name + + +class LOCGroup(models.Model): + """ + Represents a group or category within the Local Organizing Committee. + """ + name = models.CharField(max_length=100) # e.g., Logistics Team, Venue Coordination + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name="loc_groups") + + def __str__(self): + return self.name + +class LOCMember(models.Model): + """ + Represents an individual member of the Local Organizing Committee. + """ + name = models.CharField(max_length=100) + link = models.CharField(default="",blank=True, max_length=100) + groups = models.ManyToManyField(LOCGroup, related_name="members", blank=True) + country = CountryField(default="GH", blank=False, blank_label='(select member’s country)') + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='loc_members') + is_lead = models.BooleanField(default=False, help_text='Indicate if this member is a lead') + + def __str__(self): + return self.name + + +class VolunteerGroup(models.Model): + """ + Represents a group or category of volunteers. + """ + name = models.CharField(max_length=100) # e.g., Moderating Team, Talks & Workshop Support + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name="volunteer_groups") + + def __str__(self): + return self.name + +class Volunteer(models.Model): + """ + Represents an individual volunteer, associated with one or more groups. + """ + name = models.CharField(max_length=100) + link = models.CharField(default="",blank=True, max_length=100) + groups = models.ManyToManyField(VolunteerGroup, related_name="volunteers") + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name="volunteers") + + def __str__(self): + return self.name \ No newline at end of file diff --git a/pycons-site/about/tests.py b/pycons-site/about/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/about/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/about/urls.py b/pycons-site/about/urls.py new file mode 100644 index 0000000..af2f55b --- /dev/null +++ b/pycons-site/about/urls.py @@ -0,0 +1,22 @@ +from django.conf.urls.static import static +from django.contrib import admin +from django.urls import include, path, re_path +from . import views +from .views import AboutView +from .views import * +from django.conf import settings +from django.contrib.auth.decorators import login_required + +app_name = 'about' +urlpatterns = [ + # Adjusted to include a year parameter + path('', view=views.about, name='about_home'), + path('/about/update//edit/', views.about_edit, name='about_edit'), + + + # Adjusted team, venue, and travel advice URLs to include a year parameter + path('team/', teams_view, name='team'), + path('venue/', view=views.venue, name='venue'), + path('travel-advice/', view=views.travel_advice, name='travel_advice'), + path('platform/', view=views.hopin, name='hopin'), +] diff --git a/pycons-site/about/views.py b/pycons-site/about/views.py new file mode 100644 index 0000000..1365c16 --- /dev/null +++ b/pycons-site/about/views.py @@ -0,0 +1,193 @@ +#home views +import operator + +# Core Django imports. +from django.shortcuts import render, get_object_or_404, redirect +from django.contrib import messages +from django.db.models import Q +from django.template import RequestContext +from django.views.generic.detail import DetailView +from django.views.generic import ( + DetailView, + ListView, +) +from django.utils import timezone +from django.contrib.auth.decorators import login_required +from functools import reduce + +# About application imports. +from .models import * +from .models import About, Venue +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger + +# Included by me (3rd Parties and more) +from django.contrib.auth.models import User +from datetime import datetime +from .forms import * + +from django.contrib.auth import authenticate, login, get_user_model +from django.shortcuts import get_object_or_404, render, redirect +from django.views.generic.edit import UpdateView +from django.views.generic.base import TemplateView +from django.urls import reverse_lazy +from django.views import generic +from django.http import HttpResponseRedirect + +from about.mixins import EditOwnAboutMixin, EditOwnLoginMixin + + +# Create your views here. +def redirect_to_current_year_about(request): + current_year = timezone.now().year + return redirect('abouts', year=current_year) + +def current_event_year(year=None): + if year is None: + year = timezone.now().year + return EventYear.objects.filter(year=year).first() + +def about(request, year): + event_year = get_object_or_404(EventYear, year=year) + abouts = About.objects.filter(event_year=event_year).order_by('-date_created') + template_name = f'{year}/about/about.html' + return render(request, template_name, {'abouts': abouts, 'event_year': event_year}) + +def about_edit(request, pk, year): + about = get_object_or_404(About, pk=pk) + event_year = get_object_or_404(EventYear, year=year) + if request.method == "POST": + form = AboutForm(request.POST, instance=about) + if form.is_valid(): + about.save() + return redirect('about:about_home', year=event_year.year) + else: + form = AboutForm(instance=about) + template_name = f'{year}/about/update_about.html' + return render(request, template_name, {'form': form, 'event_year': event_year}) + +class AboutView(EditOwnAboutMixin, UpdateView): + form_class = AboutForm + model = About + + def get_template_names(self): + year = self.kwargs.get('year') + return [f"{year}/about/update_about.html"] + + def get_success_url(self): + year = self.kwargs.get('year') + return reverse_lazy('about:about_home', kwargs={'year': year}) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + year = self.kwargs.get('year') + context['year'] = year + return context + +def venue(request, year): + event_year = get_object_or_404(EventYear, year=year) + venues = Venue.objects.filter(event_year=event_year).order_by('-date_created') + template_name = f'{year}/about/venue.html' + return render(request, template_name, {'venues': venues, 'event_year': event_year}) + +def teams_view(request, year): + event_year = get_object_or_404(EventYear, year=year) + + # International Organizing Committee + ioc_groups = IOCGroup.objects.filter(event_year=event_year) + + # Prepare IOC members per group with leads and non-leads + ioc_groups_with_members = [] + for group in ioc_groups: + leads = group.iocs.filter(event_year=event_year, is_lead=True).order_by('name') + non_leads = group.iocs.filter(event_year=event_year, is_lead=False).order_by('name') + ioc_groups_with_members.append({ + 'group': group, + 'leads': leads, + 'non_leads': non_leads, + }) + + # Fetch IOC members not in any group + ioc_ungrouped_members = IOCMember.objects.filter( + event_year=event_year, + groups__isnull=True + ).order_by('name') + + if ioc_ungrouped_members.exists(): + # Separate leads and non-leads + ungrouped_leads = ioc_ungrouped_members.filter(is_lead=True) + ungrouped_non_leads = ioc_ungrouped_members.filter(is_lead=False) + + # Create a pseudo-group for ungrouped members + pseudo_group = IOCGroup(name='Other Committee Members') + + ioc_groups_with_members.append({ + 'group': pseudo_group, + 'leads': ungrouped_leads, + 'non_leads': ungrouped_non_leads, + }) + + # Local Organizing Committee + loc_groups = LOCGroup.objects.filter(event_year=event_year) + + # Prepare LOC members per group with leads and non-leads + loc_groups_with_members = [] + for group in loc_groups: + leads = group.members.filter(event_year=event_year, is_lead=True).order_by('name') + non_leads = group.members.filter(event_year=event_year, is_lead=False).order_by('name') + loc_groups_with_members.append({ + 'group': group, + 'leads': leads, + 'non_leads': non_leads, + }) + + # Fetch LOC members not in any group (if applicable) + loc_ungrouped_members = LOCMember.objects.filter( + event_year=event_year, + groups__isnull=True + ).order_by('name') + + if loc_ungrouped_members.exists(): + # Separate leads and non-leads + loc_ungrouped_leads = loc_ungrouped_members.filter(is_lead=True) + loc_ungrouped_non_leads = loc_ungrouped_members.filter(is_lead=False) + + # Create a pseudo-group for ungrouped members + pseudo_group = LOCGroup(name='Other Committee Members') + + loc_groups_with_members.append({ + 'group': pseudo_group, + 'leads': loc_ungrouped_leads, + 'non_leads': loc_ungrouped_non_leads, + }) + + # Volunteers + volunteer_groups = VolunteerGroup.objects.filter(event_year=event_year) + volunteers = Volunteer.objects.filter(event_year=event_year).order_by('name') + + context = { + 'event_year': event_year, + 'ioc_groups_with_members': ioc_groups_with_members, + 'loc_groups_with_members': loc_groups_with_members, + 'volunteer_groups': volunteer_groups, + 'volunteers': volunteers, + } + + template_name = f'{year}/about/teams.html' + + return render(request, template_name, context) + + +def travel_advice(request, year): + event_year = get_object_or_404(EventYear, year=year) + travel_advices = Travel_Advice.objects.filter(event_year=event_year).order_by('-date_created') + template_name = f'{year}/about/travel.html' + return render(request, template_name, {'travel_advices': travel_advices, 'event_year': event_year}) + + + #2020 + + +def hopin(request): + context = {"about": "active"} + template = '2020/hopin.html' + return render(request, template, context) \ No newline at end of file diff --git a/pycons-site/apps.py b/pycons-site/apps.py new file mode 100644 index 0000000..052a88d --- /dev/null +++ b/pycons-site/apps.py @@ -0,0 +1,34 @@ +from django.apps import AppConfig + +class PyconsSiteConfig(AppConfig): + name = 'pycons_site' + label = 'pycons_site' + verbose_name = "PyCons Site" + + def ready(self): + from django.conf import apps + # Dynamically include all sub-apps here + sub_apps = [ + 'about', + 'cms', + 'coc', + 'conference_schedule', + 'contact', + 'event', + 'faq', + 'fin_aid', + 'health_safety_guideline', + 'home', + 'privacypolicy', + 'registration', + 'schedule', + 'speakers', + 'sponsors', + 'sponsor_us', + 'talks', + 'tickets', + ] + + # Automatically include sub-apps in INSTALLED_APPS + for app in sub_apps: + apps.get_app_config('pycons_site').add_app_config(f'pycons_site.{app}') diff --git a/pycons-site/cms/__init__.py b/pycons-site/cms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/cms/admin.py b/pycons-site/cms/admin.py new file mode 100644 index 0000000..b795867 --- /dev/null +++ b/pycons-site/cms/admin.py @@ -0,0 +1,7 @@ +from django.contrib import admin +from .models import Page + +@admin.register(Page) +class PageAdmin(admin.ModelAdmin): + list_display = ('page_name', 'page_title', 'slug', 'created_at', 'updated_at') + search_fields = ('page_name', 'content') diff --git a/pycons-site/cms/apps.py b/pycons-site/cms/apps.py new file mode 100644 index 0000000..a3f8532 --- /dev/null +++ b/pycons-site/cms/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CmsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'cms' diff --git a/pycons-site/cms/models.py b/pycons-site/cms/models.py new file mode 100644 index 0000000..281a291 --- /dev/null +++ b/pycons-site/cms/models.py @@ -0,0 +1,42 @@ +from django.db import models +from django.urls import reverse +from django.conf import settings +from django.contrib.auth.models import User +from markdownx.models import MarkdownxField +from django.utils import timezone +from markdownx.models import MarkdownxField +from django.utils import timezone +from embed_video.fields import EmbedVideoField +from home.models import EventYear +from django_countries.fields import CountryField +from PIL import Image +from io import BytesIO +import os +from django.core.files.base import ContentFile +from django.utils.text import slugify + + +class Page(models.Model): + page_name = models.CharField(max_length=200, default='') + page_title = models.CharField(max_length=200, default='') + meta_og_image = models.ImageField(help_text="Upload your cover image or leave blank to use our default image", default="meta-image.png", null=True, blank=True, upload_to='ticket_page') + slug = models.SlugField(max_length=200, unique=True, blank=True) + font_icon_code = models.CharField(max_length=200, default='fa-brands fa-python', help_text = "The font icon for the page, use the following format - fa-brands fa-python") + content = MarkdownxField(default='', help_text = "[Supports Markdown] - Content for the page", null=False, blank=False) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='pages') + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def save(self, *args, **kwargs): + if not self.slug: + self.slug = slugify(self.page_name) + super(Page, self).save(*args, **kwargs) + + def __str__(self): + return self.page_name + + @property + def image_url(self): + if self.meta_og_image: + return self.meta_og_image.url + return os.path.join(settings.STATIC_URL, 'default_image.png') diff --git a/pycons-site/cms/tests.py b/pycons-site/cms/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/cms/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/cms/urls.py b/pycons-site/cms/urls.py new file mode 100644 index 0000000..9794f6d --- /dev/null +++ b/pycons-site/cms/urls.py @@ -0,0 +1,6 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('/', views.page_view, name='page_view'), +] diff --git a/pycons-site/cms/views.py b/pycons-site/cms/views.py new file mode 100644 index 0000000..4b47289 --- /dev/null +++ b/pycons-site/cms/views.py @@ -0,0 +1,28 @@ +from django.shortcuts import render, get_object_or_404 +from .models import Page +from home.models import EventYear +from django.utils.html import strip_tags + +def page_view(request, year, slug): + event_year = get_object_or_404(EventYear, year=year) + page = get_object_or_404(Page, slug=slug, event_year=event_year) + + # Generate metadata if not explicitly provided + meta_title = page.meta_title or page.page_title or page.page_name + meta_description = page.meta_description or strip_tags(page.content)[:160] # Get the first 160 characters of content as a description + meta_author = page.meta_author or "PyCon Africa" + meta_og_image = page.meta_og_image or 'default_image_url' # Provide a default image URL if not set + + context = { + 'page': page, + 'meta_title': meta_title, + 'meta_description': meta_description, + 'meta_author': meta_author, + 'meta_og_image': meta_og_image, + } + template_name = f'{year}/pages/page.html' + return render(request, template_name, context) + +def page_view(request, year, slug): + page = get_object_or_404(Page, slug=slug) + return render(request, f'{year}/pages/page.html', {'page': page}) diff --git a/pycons-site/coc/__init__.py b/pycons-site/coc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/coc/admin.py b/pycons-site/coc/admin.py new file mode 100644 index 0000000..6b0bc46 --- /dev/null +++ b/pycons-site/coc/admin.py @@ -0,0 +1,21 @@ +# Core Django imports. +from django.contrib import admin + +# Blog application imports. +from .models import Coc + +class CocAdmin(admin.ModelAdmin): + + list_display = ('title', 'user', 'event_year', 'date_created') + ordering = ['-date_created', ] + + exclude = ('user',) # Exclude the user field from the admin form + + def save_model(self, request, obj, form, change): + if not obj.pk: # If the object is being created (and not modified) + obj.user = request.user # Set the user to the current user + super().save_model(request, obj, form, change) + +# Registers the Cocmodel at the admin backend. +admin.site.register(Coc, CocAdmin) + diff --git a/pycons-site/coc/apps.py b/pycons-site/coc/apps.py new file mode 100644 index 0000000..6815fe3 --- /dev/null +++ b/pycons-site/coc/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CocConfig(AppConfig): + name = 'coc' diff --git a/pycons-site/coc/forms.py b/pycons-site/coc/forms.py new file mode 100644 index 0000000..6b68726 --- /dev/null +++ b/pycons-site/coc/forms.py @@ -0,0 +1,43 @@ +""" +Forms and validation code for user registration. + +Note that all of these forms assume Django's bundle default ``User`` +model; since it's not possible for a form to anticipate in advance the +needs of custom user models, you will need to write your own forms if +you're using a custom model. + +""" +from django import forms + +from registration.users import UserModel + +# Third Parties +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Submit +from django_recaptcha.fields import ReCaptchaField + + +User = UserModel() + + +from .models import Coc + + + + +class CocForm(forms.ModelForm): + captcha = ReCaptchaField() + + class Meta: + model = Coc + fields = ('title', 'code_of_conduct', 'user',) + + def __init__(self, *args, **kwargs): + super(CocForm, self).__init__(*args, **kwargs) + self.fields['user'].disabled = True + + self.helper = FormHelper() + self.helper.form_id = 'id-Crispy_CocForm' + self.helper.form_class = 'form-horizontal' + self.helper.add_input(Submit('update', 'Coc ')) + diff --git a/pycons-site/coc/migrations/0001_initial.py b/pycons-site/coc/migrations/0001_initial.py new file mode 100644 index 0000000..a92a209 --- /dev/null +++ b/pycons-site/coc/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 3.2 on 2022-08-23 02:29 + +from django.conf import settings +import django.contrib.auth.models +from django.db import migrations, models +import django.db.models.deletion +import markdownx.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Coc', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Code of Conduct PyCon Africa', max_length=250)), + ('code_of_conduct', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - COC PyCon Africa.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(default=django.contrib.auth.models.User, on_delete=django.db.models.deletion.CASCADE, related_name='coc', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/pycons-site/coc/migrations/0002_alter_coc_id.py b/pycons-site/coc/migrations/0002_alter_coc_id.py new file mode 100644 index 0000000..29b332b --- /dev/null +++ b/pycons-site/coc/migrations/0002_alter_coc_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-02-29 23:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('coc', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='coc', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/pycons-site/coc/migrations/0003_coc_event_year.py b/pycons-site/coc/migrations/0003_coc_event_year.py new file mode 100644 index 0000000..c00472d --- /dev/null +++ b/pycons-site/coc/migrations/0003_coc_event_year.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.2 on 2024-03-02 22:20 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('coc', '0002_alter_coc_id'), + ('home', '0008_pyconevent'), + ] + + operations = [ + migrations.AddField( + model_name='coc', + name='event_year', + field=models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='cocs', to='home.eventyear'), + ), + ] diff --git a/pycons-site/coc/migrations/__init__.py b/pycons-site/coc/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/coc/mixins.py b/pycons-site/coc/mixins.py new file mode 100644 index 0000000..421048a --- /dev/null +++ b/pycons-site/coc/mixins.py @@ -0,0 +1,23 @@ +from django.core.exceptions import PermissionDenied + +class EditOwnCocMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnCocMixin, self).get_object(*args, **kwargs) + self.check_permission(obj) + return obj + def check_permission(self, obj): + if hasattr(obj, 'user'): + obj = obj.user + if obj == self.request.user: + return + else: + raise PermissionDenied() + +class EditOwnLoginMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnLoginMixin, self).get_object(*args, **kwargs) + if obj.email == self.request.user.email: + return obj + else: + raise PermissionDenied + diff --git a/pycons-site/coc/models.py b/pycons-site/coc/models.py new file mode 100644 index 0000000..e65fecc --- /dev/null +++ b/pycons-site/coc/models.py @@ -0,0 +1,25 @@ +from django.db import models +from django.urls import reverse +from django.conf import settings +from django.contrib.auth.models import User +from markdownx.models import MarkdownxField +from django.utils import timezone +from home.models import EventYear + + +class Coc(models.Model): + title = models.CharField(max_length=250, null=False, blank=False, help_text='Code of Conduct') + code_of_conduct = MarkdownxField(default='', help_text = "[Supports Markdown] - COC.", null=False, blank=False + ) + user = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='coc',default=User) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='cocs') + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("coc") + \ No newline at end of file diff --git a/pycons-site/coc/tests.py b/pycons-site/coc/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/coc/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/coc/urls.py b/pycons-site/coc/urls.py new file mode 100644 index 0000000..6b8b2b9 --- /dev/null +++ b/pycons-site/coc/urls.py @@ -0,0 +1,8 @@ +from django.urls import path +from . import views + +app_name = 'coc' +urlpatterns = [ + path('', views.coc, name='coc') + +] \ No newline at end of file diff --git a/pycons-site/coc/views.py b/pycons-site/coc/views.py new file mode 100644 index 0000000..a5d9699 --- /dev/null +++ b/pycons-site/coc/views.py @@ -0,0 +1,85 @@ +#home views +import operator + +# Core Django imports. +from django.shortcuts import render, get_object_or_404, redirect +from django.contrib import messages +from django.db.models import Q +from django.template import RequestContext +from django.views.generic.detail import DetailView +from django.views.generic import ( + DetailView, + ListView, +) +from django.utils import timezone +from django.contrib.auth.decorators import login_required +from functools import reduce + + +# Included by me (3rd Parties and more) +from django.contrib.auth.models import User +from datetime import datetime +from django.contrib.auth import authenticate, login, get_user_model +from django.views.generic.edit import UpdateView +from django.views.generic.base import TemplateView +from django.urls import reverse_lazy +from django.views import generic +from django.http import HttpResponseRedirect + +# Coc application imports. +from .models import Coc +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger + +from coc.mixins import EditOwnCocMixin, EditOwnLoginMixin +from .forms import CocForm +from home.models import EventYear + + + +def coc(request, year): + event_year = get_object_or_404(EventYear, year=year) + cocs = Coc.objects.filter(event_year=event_year).order_by('-date_created') + template_name = f'{year}/coc/coc.html' # Dynamically set based on the year + return render(request, template_name, {'cocs': cocs, 'event_year': event_year}) + +def coc_edit(request, pk): + coc = get_object_or_404(Coc, pk=pk) + if request.method == "POST": + form = CocForm(request.POST, instance=coc) + if form.is_valid(): + coc = form.save(commit=False) + coc.user = request.user + coc.date_updated = timezone.now() + coc.save() + return redirect('coc:coc_home') + else: + form = CocForm(instance=coc) + return render(request, '2022/coc/update_coc.html', {'form': form}) + + +class CocView(EditOwnCocMixin, UpdateView): + form_class = CocForm + model = Coc + template_name = "2022/coc/update_coc.html" + success_url = reverse_lazy('coc:coc_home') + + def get_context_data(self, **kwargs): + context = super(UpdateView, self).get_context_data(**kwargs) + context['title'] = 'Coc' + context['year'] = datetime.now().year + try: + context['coc'] = Coc() + except Coc.DoesNotExist: + context['coc'] = '' + return context + +def coc_update_view(UpdateView): + obj = get_object_or_404(Coc, id=id) + form = CocForm(UpdateView.POST or None, instance=obj) + if form.is_valid(): + form.save() + context = { + 'form': form + } + return render(UpdateView, "2022/coc/update_coc.html", context) + diff --git a/pycons-site/conference_schedule/__init__.py b/pycons-site/conference_schedule/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/conference_schedule/admin.py b/pycons-site/conference_schedule/admin.py new file mode 100644 index 0000000..356c288 --- /dev/null +++ b/pycons-site/conference_schedule/admin.py @@ -0,0 +1,71 @@ +from django.contrib import admin +from conference_schedule.models import Schedule, Room, Day, ScheduleVisibility +from conference_schedule.forms import TalkScheduleForm + +from .models import Schedule +class TalkScheduleAdmin(admin.ModelAdmin): + form = TalkScheduleForm # Use the custom form + + list_display = ('get_talk_or_event', 'allocated_room', 'conference_day', 'concurrent_talk', 'start_time', 'end_time', 'is_an_event', 'is_a_keynote_speaker', 'is_a_panel') + list_editable = ['allocated_room', 'conference_day', 'start_time', 'end_time', 'is_an_event', 'concurrent_talk', 'is_a_keynote_speaker', 'is_a_panel'] + + # Filters to enhance usability + list_filter = ['conference_day', 'allocated_room', 'is_a_keynote_speaker', 'is_a_panel', 'concurrent_talk'] + + # Adding search capabilities + search_fields = ['talk__title', 'event'] + + # Ensure ordering is by conference day and start time + ordering = ['conference_day__actual_date', 'start_time'] + + def get_queryset(self, request): + queryset = super().get_queryset(request) + return queryset.select_related('talk', 'allocated_room', 'conference_day').order_by('conference_day__actual_date', 'start_time') + + # Custom admin actions + actions = ['mark_as_keynote', 'mark_as_panel'] + + def mark_as_keynote(self, request, queryset): + queryset.update(is_a_keynote_speaker=True) + self.message_user(request, "Selected talks are now marked as keynote speakers.") + + def mark_as_panel(self, request, queryset): + queryset.update(is_a_panel=True) + self.message_user(request, "Selected talks are now marked as panels.") + + mark_as_keynote.short_description = "Mark selected talks as keynote speakers" + mark_as_panel.short_description = "Mark selected talks as panels" + + # Custom method to display either the talk or event in list_display + def get_talk_or_event(self, obj): + if obj.is_an_event: + return obj.event + elif obj.talk: + return obj.talk.title + return "N/A" + + get_talk_or_event.short_description = 'Talk/Event' + + +# Register the Schedule model with the updated admin +admin.site.register(Schedule, TalkScheduleAdmin) + +admin.site.register(Room) +admin.site.register(Day) + + +@admin.register(ScheduleVisibility) +class ScheduleVisibilityAdmin(admin.ModelAdmin): + list_display = ('is_live',) + actions = ['make_live', 'make_not_live'] + + def make_live(self, request, queryset): + queryset.update(is_live=True) + self.message_user(request, "The schedule is now live and visible to all users.") + + def make_not_live(self, request, queryset): + queryset.update(is_live=False) + self.message_user(request, "The schedule is now hidden from all users except superusers.") + + make_live.short_description = "Make schedule live" + make_not_live.short_description = "Hide schedule from users" diff --git a/pycons-site/conference_schedule/apps.py b/pycons-site/conference_schedule/apps.py new file mode 100644 index 0000000..4f3a016 --- /dev/null +++ b/pycons-site/conference_schedule/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ConferenceScheduleConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'conference_schedule' diff --git a/pycons-site/conference_schedule/forms.py b/pycons-site/conference_schedule/forms.py new file mode 100644 index 0000000..75c947d --- /dev/null +++ b/pycons-site/conference_schedule/forms.py @@ -0,0 +1,90 @@ +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Field, Submit, Div, Row, Column +from django import forms +from .models import Schedule, Proposal +from django.db import models + + + + + + +class TalkScheduleForm(forms.ModelForm): + class Meta: + model = Schedule # Ensure this is from the correct app + fields = '__all__' + widgets = { + 'conference_day': forms.Select(attrs={'class': 'form-control'}), + 'talk': forms.Select(attrs={'class': 'form-control'}), + 'event': forms.TextInput(attrs={'class': 'form-control'}), + 'start_time': forms.TimeInput(attrs={'class': 'form-control', 'type': 'time', 'step': '60'}), + 'end_time': forms.TimeInput(attrs={'class': 'form-control', 'type': 'time', 'step': '60'}), + 'day_session': forms.Select(attrs={'class': 'form-control'}), + 'allocated_room': forms.Select(attrs={'class': 'form-control'}), + 'event_url': forms.URLInput(attrs={'class': 'form-control', 'placeholder': 'Enter event URL'}), + 'external_url': forms.URLInput(attrs={'class': 'form-control', 'placeholder': 'Enter external URL'}), + } + + def __init__(self, *args, **kwargs): + super(TalkScheduleForm, self).__init__(*args, **kwargs) + + # Initialize Crispy Forms helper + self.helper = FormHelper() + self.helper.form_method = 'post' + self.helper.layout = Layout( + Row( + Column(Field('conference_day'), css_class='form-group col-md-6 mb-0'), + Column(Field('talk'), css_class='form-group col-md-6 mb-0'), + ), + Row( + Column(Field('event'), css_class='form-group col-md-12 mb-0'), + ), + Row( + Column(Field('start_time'), css_class='form-group col-md-6 mb-0'), + Column(Field('end_time'), css_class='form-group col-md-6 mb-0'), + ), + Row( + Column(Field('day_session'), css_class='form-group col-md-6 mb-0'), + Column(Field('allocated_room'), css_class='form-group col-md-6 mb-0'), + ), + Row( + Column(Field('event_url'), css_class='form-group col-md-6 mb-0'), + Column(Field('external_url'), css_class='form-group col-md-6 mb-0'), + ), + Submit('submit', 'Save Schedule', css_class='btn btn-primary') + ) + + # Fetch the instance being edited + instance = kwargs.get('instance') + + # Fetch talks already scheduled + scheduled_talks = Schedule.objects.filter(talk__isnull=False).values_list('talk_id', flat=True) + + # If we're editing a schedule, allow the current talk to be in the dropdown + if instance and instance.talk: + current_talk_id = instance.talk.proposal_id # Assuming 'proposal_id' is the primary key + # Exclude all scheduled talks except the current one + available_talks = Proposal.objects.filter( + status='A', user_response='A' + ).exclude(pk__in=scheduled_talks).union( + Proposal.objects.filter(pk=current_talk_id) + ) + else: + # Exclude all already scheduled talks + available_talks = Proposal.objects.filter( + status='A', user_response='A' + ).exclude(pk__in=scheduled_talks) + + # Add available talks to the dropdown + talk_choices = [(talk.pk, talk.title) for talk in available_talks] + self.fields['talk'].choices = [('', 'Select a talk')] + talk_choices + + # Set placeholders and help texts dynamically + self.fields['event'].widget.attrs.update({'placeholder': 'Enter event name'}) + self.fields['talk'].widget.attrs.update({'placeholder': 'Select a talk'}) + + + + + + \ No newline at end of file diff --git a/pycons-site/conference_schedule/models.py b/pycons-site/conference_schedule/models.py new file mode 100644 index 0000000..4a18a56 --- /dev/null +++ b/pycons-site/conference_schedule/models.py @@ -0,0 +1,121 @@ +from talks.models import Proposal +from event.models import * +from django.core.exceptions import ValidationError +import datetime + + + +class Day(models.Model): + conference_day = models.CharField(max_length=30, unique=True, help_text="The name of the conference day (e.g., Day 1, Day 2).") + actual_date = models.DateField(help_text="The actual date of the conference day.", default=datetime.date(2024, 9, 9)) + + class Meta: + verbose_name = "Conference Day" + verbose_name_plural = "Conference Days" + ordering = ['actual_date'] + + def __str__(self): + return self.actual_date.strftime('%Y-%m-%d') + + +class Room(models.Model): + room_name = models.CharField(max_length=50, unique=True, null=True, blank=True, help_text="The name or number of the conference room.") + + class Meta: + verbose_name = "Room" + verbose_name_plural = "Rooms" + ordering = ['room_name'] + + def __str__(self): + return self.room_name + + +class Schedule(models.Model): + DAY_SESSIONS = ( + ('Morning', 'Morning'), + ('Afternoon', 'Afternoon'), + ('Evening', 'Evening'), + ) + + conference_day = models.ForeignKey(Day, on_delete=models.CASCADE) + talk = models.ForeignKey(Proposal, on_delete=models.CASCADE, blank=True, null=True, help_text="Select a Talk if it's a Speaker giving a talk") + event = models.CharField(max_length=255, default="", blank=True, help_text="The name of the event or activity.") + event_description = models.TextField(blank=True, help_text="A short description of the event.") + is_an_event = models.BooleanField(default=False, help_text="Indicate if this is an event instead of a talk.") + fa_icon = models.CharField(max_length=100, default='', blank=True) + event_url = models.CharField(max_length=250, default="", blank=True, help_text='URL to the event') + external_url = models.CharField(max_length=250, default="", blank=True, help_text='External link to the event') + + start_time = models.TimeField(blank=True, null=True) + end_time = models.TimeField(blank=True, null=True) + + day_session = models.CharField(max_length=10, choices=DAY_SESSIONS, default='', blank=True) + allocated_room = models.ForeignKey(Room, on_delete=models.CASCADE, blank=True, null=True) + rowspan = models.CharField(max_length=10, default='', blank=True, help_text="Use to determine how this talk fits in its time allocation") + concurrent_talk = models.BooleanField(default=False) + is_a_keynote_speaker = models.BooleanField(default=False) + is_a_panel = models.BooleanField(default=False) + + class Meta: + managed = True + verbose_name_plural = "Talk Schedules" + indexes = [ + models.Index(fields=['start_time', 'end_time']), + ] + + permissions = [ + ("can_manage_schedule", "Can manage schedules"), + ] + + def __str__(self): + if self.is_an_event: + return f"{self.event} in {self.allocated_room}" if self.allocated_room else f"{self.event}" + + if self.talk: + if self.talk.speakers.exists(): + # Safely retrieve either the profile name or fall back to "PyCon " + speakers_names = ', '.join([getattr(speaker.user_profile, 'name', 'PyCon ') for speaker in self.talk.speakers.all()]) + return f"{self.talk.title} by {speakers_names}" + else: + return self.talk.title + + return "No event or talk assigned" + + def clean(self): + super().clean() + if not self.talk and not self.event: + raise ValidationError('Either a talk or an event must be selected.') + if self.talk and self.event: + raise ValidationError('You cannot have both a talk and an event in the same schedule entry.') + if self.is_an_event and not self.event: + raise ValidationError('You must provide an event name if this is an event.') + if self.is_an_event and self.talk and self.talk.speakers.exists(): + raise ValidationError('Speakers cannot be assigned to events.') + if self.start_time and self.end_time and self.start_time >= self.end_time: + raise ValidationError('The start time must be before the end time.') + + def save(self, *args, **kwargs): + self.clean() + conference_date = self.conference_day.actual_date + if conference_date and self.start_time and self.end_time: + start_datetime = timezone.datetime.combine(conference_date, self.start_time) + end_datetime = timezone.datetime.combine(conference_date, self.end_time) + if timezone.is_naive(start_datetime): + start_datetime = timezone.make_aware(start_datetime) + if timezone.is_naive(end_datetime): + end_datetime = timezone.make_aware(end_datetime) + self.start_time = start_datetime + self.end_time = end_datetime + super().save(*args, **kwargs) + + + +class ScheduleVisibility(models.Model): + is_live = models.BooleanField(default=False, help_text="Indicates if the schedule is live and visible to all users.") + + class Meta: + verbose_name = "Schedule Visibility" + verbose_name_plural = "Schedule Visibility" + + def __str__(self): + return "Live" if self.is_live else "Not Live" diff --git a/pycons-site/conference_schedule/templatetags/__init__.py b/pycons-site/conference_schedule/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/conference_schedule/templatetags/schedule_hours.py b/pycons-site/conference_schedule/templatetags/schedule_hours.py new file mode 100644 index 0000000..ac7319d --- /dev/null +++ b/pycons-site/conference_schedule/templatetags/schedule_hours.py @@ -0,0 +1,14 @@ +from django import template +from django.template.defaultfilters import stringfilter + +register = template.Library() + + +@register.filter +@stringfilter +def upto(value, delimiter=None): + return value.split(delimiter)[0] + + +upto.is_safe = True + diff --git a/pycons-site/conference_schedule/templatetags/schedule_tags.py b/pycons-site/conference_schedule/templatetags/schedule_tags.py new file mode 100644 index 0000000..fda56f7 --- /dev/null +++ b/pycons-site/conference_schedule/templatetags/schedule_tags.py @@ -0,0 +1,67 @@ +from django import template +from ..models import Schedule, Day, ScheduleVisibility +from home.models import EventYear +from django.template.loader import get_template +from django.template import TemplateDoesNotExist +from django import template +from django.template.loader import get_template, TemplateDoesNotExist +from ..models import ScheduleVisibility, Schedule, Day +from django.db import models + + +register = template.Library() + + + + +@register.filter +def get_item(dictionary, key): + """Custom template filter to get an item from a dictionary.""" + return dictionary.get(key) + + + + +@register.inclusion_tag('2024/schedule/schedule_home.html', takes_context=True) +def schedule_preview(context, year, limit=3): + request = context['request'] + event_year = EventYear.objects.get(year=year) + + # Check visibility of the schedule + visibility = ScheduleVisibility.objects.first() + if visibility is None: + visibility = ScheduleVisibility.objects.create(is_live=False) + + # If the schedule is not live and the user is not a superuser, return an empty schedule + if not visibility.is_live and not request.user.is_superuser: + return { + 'day_schedules': [], + 'year': year, + 'limit': limit, + 'is_schedule_live': visibility.is_live or request.user.is_superuser, + } + + # Fetch all conference days and order by the actual date of the conference day + days = Day.objects.all().order_by('actual_date') + day_schedules = [] + + for day in days: + # Fetch both talks and events for each day, limited to the first `limit` items + schedules = Schedule.objects.filter( + conference_day=day + ).filter( + models.Q(talk__event_year=event_year) | models.Q(is_an_event=True) + ).select_related('talk', 'talk__user').prefetch_related('talk__speakers').order_by('start_time')[:limit] + + # Add each day along with its schedules (even if there are no schedules) + day_schedules.append({ + 'day': day, + 'schedules': schedules + }) + + return { + 'day_schedules': day_schedules, + 'year': year, + 'limit': limit, + 'is_schedule_live': visibility.is_live, + } \ No newline at end of file diff --git a/pycons-site/conference_schedule/templatetags/schedule_urlify.py b/pycons-site/conference_schedule/templatetags/schedule_urlify.py new file mode 100644 index 0000000..9b9d27f --- /dev/null +++ b/pycons-site/conference_schedule/templatetags/schedule_urlify.py @@ -0,0 +1,14 @@ +# Third-party library imports +import urllib.parse as encode + +# Core Django imports +from django import template + +register = template.Library() + + +@register.filter +def urlify(value): + encode_url = encode.quote_plus(value) + return encode_url + diff --git a/pycons-site/conference_schedule/tests.py b/pycons-site/conference_schedule/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/conference_schedule/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/conference_schedule/urls.py b/pycons-site/conference_schedule/urls.py new file mode 100644 index 0000000..881e10d --- /dev/null +++ b/pycons-site/conference_schedule/urls.py @@ -0,0 +1,15 @@ +from django.urls import path, include +from conference_schedule import views # Update the import to reference the new app +from conference_schedule.views import ScheduleDetailView, create_talk_schedule # Import views correctly + + +app_name = 'conference_schedule' # Update the app name to reflect the new app + +urlpatterns = [ + path('', views.schedule, name='schedule'), + path('/', ScheduleDetailView.as_view(), name='schedule_detail'), + # path('/edit/', views.schedule_edit, name='schedule_edit'), + path('hitcount/', include(('hitcount.urls', 'hitcount'), namespace='hitcount')), + path('create/', create_talk_schedule, name='create_talk_schedule'), + path('new/', create_talk_schedule, name='create_new_talk_schedule'), +] diff --git a/pycons-site/conference_schedule/views.py b/pycons-site/conference_schedule/views.py new file mode 100644 index 0000000..67646b3 --- /dev/null +++ b/pycons-site/conference_schedule/views.py @@ -0,0 +1,159 @@ +from __future__ import absolute_import +from django.http import HttpRequest +from django.shortcuts import render, redirect +from django.shortcuts import get_object_or_404 +from hitcount.views import HitCountDetailView +from registration.models import Profile +from talks.models import Proposal +from .forms import TalkScheduleForm +from django.contrib.auth.decorators import permission_required +from django.contrib.auth.decorators import login_required +from .models import * + +from .models import Schedule, Day + + + +def schedule(request, year): + """Renders the schedule page for a specific year.""" + assert isinstance(request, HttpRequest) + + # Fetch the event year or raise 404 if not found + event_year = get_object_or_404(EventYear, year=year) + + # Check the visibility setting + visibility = ScheduleVisibility.objects.first() + if visibility is None: + visibility = ScheduleVisibility.objects.create(is_live=False) + + # Fetch and order the conference days + days = Day.objects.all().order_by('actual_date') + + # Prepare schedules per day + schedules = {} + for day in days: + # Fetch both talks and events for the given day and event year + schedules[day] = Schedule.objects.filter( + conference_day=day + ).filter( + models.Q(talk__event_year=event_year) | models.Q(is_an_event=True) + ).select_related('talk', 'talk__user').prefetch_related('talk__speakers').order_by('start_time') + + # Meta information for Open Graph and Twitter Cards + meta_title = f"Schedule | PyCon Africa {year}" + meta_description = f"Explore the schedule for PyCon Africa {year}, including keynotes, talks, tutorials, and more. Plan your conference experience with us." + meta_og_image = "https://res.cloudinary.com/pycon-africa/image/upload/v1722977619/website_storage_location/media/schedule_og_image.png" # Replace with a suitable image + + # Render the schedule page with days and their respective schedules + return render( + request, + f'{year}/schedule/schedule.html', + { + 'title': 'Schedule', + 'year': year, + 'days': days, + 'schedules': schedules, + 'is_schedule_live': visibility.is_live or request.user.is_superuser, + 'meta_title': meta_title, + 'meta_description': meta_description, + 'meta_og_image': meta_og_image, + } + ) + + + +class ScheduleDetailView(HitCountDetailView): + model = Schedule + context_object_name = 'schedule' + slug_field = 'proposal_id' # Ensure this field matches your model's field + count_hit = True # To count hits for hitcount feature + paginate_by = 3 + + def get_template_names(self): + year = self.kwargs.get('year') + return [f'{year}/schedule/schedule_details.html'] + + def get_context_data(self, **kwargs): + context = super(ScheduleDetailView, self).get_context_data(**kwargs) + year = self.kwargs.get('year') + event_year = get_object_or_404(EventYear, year=year) + + context.update({ + 'talks': Proposal.objects.filter(status="A", event_year=event_year), + 'speakers': Profile.objects.filter(user__proposals__event_year=event_year).distinct(), + 'schedules': Schedule.objects.filter(talk__event_year=event_year), + 'event_year': event_year + }) + return context + + +@login_required +@permission_required('conference_schedule.add_schedule', raise_exception=True) +def create_talk_schedule(request, year): + event_year = get_object_or_404(EventYear, year=year) + + if request.method == 'POST': + form = TalkScheduleForm(request.POST) + if form.is_valid(): + talk_schedule = form.save(commit=False) + + # Check if a talk was selected + if talk_schedule.talk: + talk_schedule.talk.event_year = event_year # Only set event_year if a talk is selected + + talk_schedule.save() + return redirect('conference_schedule:schedule', year=year) # Update this with the correct app namespace + else: + form = TalkScheduleForm() + + return render(request, f'{year}/schedule/create_schedule.html', {'form': form, 'year': year}) + + + +@login_required +@permission_required('conference_schedule.add_schedule', raise_exception=True) +def create_new_talk_schedule(request, year): + """ + View to create a new talk or event schedule for a specific event year. + """ + # Get the event year based on the URL parameter + event_year = get_object_or_404(EventYear, year=year) + + if request.method == 'POST': + form = TalkScheduleForm(request.POST) + + if form.is_valid(): + # Get the form instance but don’t save it to the database just yet + schedule_instance = form.save(commit=False) + + # Assign the event year to the talk if a talk is selected + if schedule_instance.talk: + schedule_instance.talk.event_year = event_year + + # Ensure start time is before end time + if schedule_instance.start_time and schedule_instance.end_time: + if schedule_instance.start_time >= schedule_instance.end_time: + form.add_error('start_time', 'Start time must be before end time.') + form.add_error('end_time', 'End time must be after start time.') + return render(request, f'{year}/schedule/create_schedule.html', {'form': form, 'year': year}) + + # Save the schedule instance after additional checks + schedule_instance.save() + + # Success message and redirection to the schedule overview page + messages.success(request, 'The talk/event has been successfully scheduled.') + return redirect('conference_schedule:schedule', year=year) + else: + # If form is not valid, render the form again with errors + messages.error(request, 'There was an error with your submission. Please check the form and try again.') + + else: + # Create an empty form for GET requests + form = TalkScheduleForm() + + # Render the create schedule page with the form + return render(request, f'{year}/schedule/new.html', { + 'form': form, + 'year': year, + 'event_year': event_year, + }) \ No newline at end of file diff --git a/pycons-site/contact/__init__.py b/pycons-site/contact/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/contact/admin.py b/pycons-site/contact/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/pycons-site/contact/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/pycons-site/contact/apps.py b/pycons-site/contact/apps.py new file mode 100644 index 0000000..1f23179 --- /dev/null +++ b/pycons-site/contact/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ContactConfig(AppConfig): + name = 'contact' diff --git a/pycons-site/contact/forms.py b/pycons-site/contact/forms.py new file mode 100644 index 0000000..88a2e22 --- /dev/null +++ b/pycons-site/contact/forms.py @@ -0,0 +1,10 @@ +from django import forms + + +# Form for the contact +class ContactForm(forms.Form): + name = forms.CharField(label="Name", required=True) + email = forms.EmailField(label="Email", required=True) + subject = forms.CharField(label="Subject", required=True) + message = forms.CharField(label="Message", widget=forms.Textarea, required=True) + diff --git a/pycons-site/contact/migrations/__init__.py b/pycons-site/contact/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/contact/models.py b/pycons-site/contact/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/pycons-site/contact/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/pycons-site/contact/templates/contact.html b/pycons-site/contact/templates/contact.html new file mode 100644 index 0000000..566549b --- /dev/null +++ b/pycons-site/contact/templates/contact.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + \ No newline at end of file diff --git a/pycons-site/contact/templates/email_success.html b/pycons-site/contact/templates/email_success.html new file mode 100644 index 0000000..566549b --- /dev/null +++ b/pycons-site/contact/templates/email_success.html @@ -0,0 +1,10 @@ + + + + + Title + + + + + \ No newline at end of file diff --git a/pycons-site/contact/tests.py b/pycons-site/contact/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/contact/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/contact/uls.py b/pycons-site/contact/uls.py new file mode 100644 index 0000000..7e90d6e --- /dev/null +++ b/pycons-site/contact/uls.py @@ -0,0 +1,9 @@ +from django.urls import path +from . import views + +app_name = 'contact' +urlpatterns = [ + path('', views.contact, name='contact'), + +] + diff --git a/pycons-site/contact/views.py b/pycons-site/contact/views.py new file mode 100644 index 0000000..b2e46dc --- /dev/null +++ b/pycons-site/contact/views.py @@ -0,0 +1,25 @@ +from django.shortcuts import render, redirect +from django.core.mail import send_mail, BadHeaderError +from django.http import HttpResponse +from .forms import ContactForm + +# Create your views here. + + +# View for the Contact Page. +def contact(request): + if request.method == "GET": + form = ContactForm() + else: + form = ContactForm(request.POST) + if form.is_valid(): + name = form.cleaned_data["name"] + email = form.cleaned_data["email"] + subject = form.cleaned_data["subject"] + message = form.cleaned_data["message"] + try: + send_mail(subject, message, email, ['admin@example.com'], name) + except BadHeaderError: + return HttpResponse("Invalid header found") + return redirect("email_success") + return render(request, 'contact.html', {"form": form}) \ No newline at end of file diff --git a/pycons-site/event/__init__.py b/pycons-site/event/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/event/admin.py b/pycons-site/event/admin.py new file mode 100644 index 0000000..e6bc149 --- /dev/null +++ b/pycons-site/event/admin.py @@ -0,0 +1,21 @@ +# Core Django imports. +from __future__ import unicode_literals +from django.contrib import admin + +# Blog application imports. +from .models import Event + + +#from sponsors.admin import SponsorInline + + + +class EventAdmin(admin.ModelAdmin): + list_display = ('name', 'description', 'location' ) + ordering = ['name', '-date_created', ] + #inlines = [SponsorInline] + + +# Registers the Event model at the admin backend. +admin.site.register(Event, EventAdmin) + diff --git a/pycons-site/event/apps.py b/pycons-site/event/apps.py new file mode 100644 index 0000000..13b1f16 --- /dev/null +++ b/pycons-site/event/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class EventConfig(AppConfig): + name = 'event' diff --git a/pycons-site/event/forms.py b/pycons-site/event/forms.py new file mode 100644 index 0000000..136182f --- /dev/null +++ b/pycons-site/event/forms.py @@ -0,0 +1,7 @@ +from django import forms +from .models import Event + +class EventForm(forms.ModelForm): + class Meta: + model = Event + fields = ['name', 'description', 'date', 'start_time', 'end_time', 'location', 'event_year', 'is_featured'] diff --git a/pycons-site/event/mixins.py b/pycons-site/event/mixins.py new file mode 100644 index 0000000..0bc96de --- /dev/null +++ b/pycons-site/event/mixins.py @@ -0,0 +1,23 @@ +from django.core.exceptions import PermissionDenied + +class EditOwnEventMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnEventMixin, self).get_object(*args, **kwargs) + self.check_permission(obj) + return obj + def check_permission(self, obj): + if hasattr(obj, 'user'): + obj = obj.user + if obj == self.request.user: + return + else: + raise PermissionDenied() + +class EditOwnLoginMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnLoginMixin, self).get_object(*args, **kwargs) + if obj.email == self.request.user.email: + return obj + else: + raise PermissionDenied + diff --git a/pycons-site/event/models.py b/pycons-site/event/models.py new file mode 100644 index 0000000..42d84c0 --- /dev/null +++ b/pycons-site/event/models.py @@ -0,0 +1,23 @@ +from django.db import models +from django.utils import timezone +from home.models import EventYear + +class Event(models.Model): + name = models.CharField(max_length=255, help_text="The name of the event or activity.") + description = models.TextField(help_text="Detailed description of the event.") + date = models.DateField(default=timezone.now, help_text="Date of the event.") + start_time = models.TimeField(default=timezone.now, help_text="Event start time.") + end_time = models.TimeField(default=timezone.now, help_text="Event end time.") + location = models.CharField(max_length=255, help_text="Location of the event.", blank=True, null=True) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, help_text="The year this event belongs to.") + is_featured = models.BooleanField(default=False, help_text="Indicates if this is a featured event.") + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + class Meta: + verbose_name = "Event" + verbose_name_plural = "Events" + ordering = ['date', 'start_time'] + + def __str__(self): + return f"{self.name} ({self.event_year.year})" diff --git a/pycons-site/event/tests.py b/pycons-site/event/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/event/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/event/urls.py b/pycons-site/event/urls.py new file mode 100644 index 0000000..caebbc6 --- /dev/null +++ b/pycons-site/event/urls.py @@ -0,0 +1,6 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('add/', views.add_event, name='add_event'), +] diff --git a/pycons-site/event/utils.py b/pycons-site/event/utils.py new file mode 100644 index 0000000..fb9d974 --- /dev/null +++ b/pycons-site/event/utils.py @@ -0,0 +1,21 @@ +from hashids import Hashids +from django.conf import settings + +hashids = Hashids(settings.HASHIDS_SALT, min_length=1) + +def h_encode(id): + return hashids.encode(id) + +def h_decode(h): + z = hashids.decode(h) + if z: + return z[0] + +class HashIdConverter: + regex = '[a-zA-Z0-9]{8,}' + + def to_python(self, value): + return h_decode(value) + + def to_url(self, value): + return h_encode(value) \ No newline at end of file diff --git a/pycons-site/event/views.py b/pycons-site/event/views.py new file mode 100644 index 0000000..a3540a6 --- /dev/null +++ b/pycons-site/event/views.py @@ -0,0 +1,17 @@ +from django.shortcuts import render, redirect +from .forms import EventForm +from .models import Event, EventYear + +def add_event(request): + if request.method == "POST": + form = EventForm(request.POST) + if form.is_valid(): + form.save() + return redirect('events_by_year', year=form.cleaned_data['event_year'].year) + else: + form = EventForm() + return render(request, 'events/add_event.html', {'form': form}) + +def events_by_year(request, year): + events = Event.objects.filter(event_year__year=year) + return render(request, 'events/events_by_year.html', {'events': events, 'year': year}) diff --git a/pycons-site/faq/__init__.py b/pycons-site/faq/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/faq/admin.py b/pycons-site/faq/admin.py new file mode 100644 index 0000000..d3f43c6 --- /dev/null +++ b/pycons-site/faq/admin.py @@ -0,0 +1,14 @@ +# Core Django imports. +from django.contrib import admin + +# Blog application imports. +from .models import FrequentlyAskedQuestion + +class FrequentlyAskedQuestionAdmin(admin.ModelAdmin): + + list_display = ('title', 'date_created') + ordering = ['-date_created', ] + +# Registers the FrequentlyAskedQuestionmodel at the admin backend. +admin.site.register(FrequentlyAskedQuestion, FrequentlyAskedQuestionAdmin) + diff --git a/pycons-site/faq/apps.py b/pycons-site/faq/apps.py new file mode 100644 index 0000000..adcba72 --- /dev/null +++ b/pycons-site/faq/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class FaqConfig(AppConfig): + name = 'faq' diff --git a/pycons-site/faq/migrations/0001_initial.py b/pycons-site/faq/migrations/0001_initial.py new file mode 100644 index 0000000..83c3c73 --- /dev/null +++ b/pycons-site/faq/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 3.2 on 2022-03-30 13:28 + +from django.conf import settings +import django.contrib.auth.models +from django.db import migrations, models +import django.db.models.deletion +import markdownx.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='FrequentlyAskedQuestion', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='FAQs PyCon Africa', max_length=250)), + ('faqs', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - FAQs PyCon Africa.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(default=django.contrib.auth.models.User, on_delete=django.db.models.deletion.CASCADE, related_name='faq', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/pycons-site/faq/migrations/0002_alter_frequentlyaskedquestion_id.py b/pycons-site/faq/migrations/0002_alter_frequentlyaskedquestion_id.py new file mode 100644 index 0000000..011c951 --- /dev/null +++ b/pycons-site/faq/migrations/0002_alter_frequentlyaskedquestion_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-02-29 23:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('faq', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='frequentlyaskedquestion', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/pycons-site/faq/migrations/__init__.py b/pycons-site/faq/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/faq/models.py b/pycons-site/faq/models.py new file mode 100644 index 0000000..a4b7222 --- /dev/null +++ b/pycons-site/faq/models.py @@ -0,0 +1,25 @@ +from django.db import models +from django.urls import reverse +from django.conf import settings +from django.contrib.auth.models import User +from markdownx.models import MarkdownxField +from django.utils import timezone +from home.models import EventYear + + +class FrequentlyAskedQuestion(models.Model): + title = models.CharField(max_length=250, null=False, blank=False, help_text='FAQs PyCon Africa') + faqs = MarkdownxField(default='', help_text = "[Supports Markdown] - FAQs PyCon Africa.", null=False, blank=False + ) + user = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='faq',default=User) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='faqs') + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("faqs") + \ No newline at end of file diff --git a/pycons-site/faq/tests.py b/pycons-site/faq/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/faq/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/faq/urls.py b/pycons-site/faq/urls.py new file mode 100644 index 0000000..ce92f93 --- /dev/null +++ b/pycons-site/faq/urls.py @@ -0,0 +1,9 @@ +from django.conf.urls.static import static +from django.contrib import admin +from django.urls import include, path +from . import views + +app_name = 'faq' +urlpatterns = [ + path('', view=views.faq, name='faq'), +] diff --git a/pycons-site/faq/views.py b/pycons-site/faq/views.py new file mode 100644 index 0000000..9bc0537 --- /dev/null +++ b/pycons-site/faq/views.py @@ -0,0 +1,29 @@ +#home views +import operator + +# Core Django imports. +from django.shortcuts import render, get_object_or_404, redirect +from django.contrib import messages +from django.db.models import Q +from django.template import RequestContext +from django.views.generic.detail import DetailView +from django.views.generic import ( + DetailView, + ListView, +) +from django.utils import timezone +from django.contrib.auth.decorators import login_required +from functools import reduce +from home.models import EventYear +# FrequentlyAskedQuestion application imports. +from .models import FrequentlyAskedQuestion +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger + + +def faq(request, year): + # Ensure the event year exists and is valid + event_year = get_object_or_404(EventYear, year=year) + # Filter FAQs based on the event year + faqs = FrequentlyAskedQuestion.objects.filter(event_year=event_year).order_by('-date_created') + # Render the template with FAQs specific to the given year + return render(request, f'{year}/faq/faq.html', {'faqs': faqs}) \ No newline at end of file diff --git a/pycons-site/fin_aid/__init__.py b/pycons-site/fin_aid/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/fin_aid/admin.py b/pycons-site/fin_aid/admin.py new file mode 100644 index 0000000..ba5b80f --- /dev/null +++ b/pycons-site/fin_aid/admin.py @@ -0,0 +1,14 @@ +# Core Django imports. +from django.contrib import admin + +# Blog application imports. +from .models import Fin_aid + +class Fin_aidAdmin(admin.ModelAdmin): + + list_display = ('title', 'date_created') + ordering = ['-date_created', ] + +# Registers the Fin_aidmodel at the admin backend. +admin.site.register(Fin_aid, Fin_aidAdmin) + diff --git a/pycons-site/fin_aid/apps.py b/pycons-site/fin_aid/apps.py new file mode 100644 index 0000000..d114caa --- /dev/null +++ b/pycons-site/fin_aid/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class fin_aidConfig(AppConfig): + name = 'fin_aid' diff --git a/pycons-site/fin_aid/forms.py b/pycons-site/fin_aid/forms.py new file mode 100644 index 0000000..f8b6c80 --- /dev/null +++ b/pycons-site/fin_aid/forms.py @@ -0,0 +1,43 @@ +""" +Forms and validation code for user registration. + +Note that all of these forms assume Django's bundle default ``User`` +model; since it's not possible for a form to anticipate in advance the +needs of custom user models, you will need to write your own forms if +you're using a custom model. + +""" +from django import forms + +from registration.users import UserModel + +# Third Parties +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Submit +from django_recaptcha.fields import ReCaptchaField + + +User = UserModel() + + +from .models import Fin_aid + + + + +class Fin_aidForm(forms.ModelForm): + captcha = ReCaptchaField() + + class Meta: + model = Fin_aid + fields = ('title', 'financial_assistance', 'user',) + + def __init__(self, *args, **kwargs): + super(Fin_aidForm, self).__init__(*args, **kwargs) + self.fields['user'].disabled = True + + self.helper = FormHelper() + self.helper.form_id = 'id-Crispy_Fin_aidForm' + self.helper.form_class = 'form-horizontal' + self.helper.add_input(Submit('update', 'Fin_aid ')) + diff --git a/pycons-site/fin_aid/migrations/0001_initial.py b/pycons-site/fin_aid/migrations/0001_initial.py new file mode 100644 index 0000000..962bf5f --- /dev/null +++ b/pycons-site/fin_aid/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 3.2 on 2022-08-14 13:36 + +from django.conf import settings +import django.contrib.auth.models +from django.db import migrations, models +import django.db.models.deletion +import markdownx.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Fin_aid', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Financial Assistance PyCon Africa', max_length=250)), + ('financial_assistance', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - Financial Assistance PyCon Africa.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(default=django.contrib.auth.models.User, on_delete=django.db.models.deletion.CASCADE, related_name='fin_aid', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/pycons-site/fin_aid/migrations/0002_fin_aid_google_form_formfacade_code.py b/pycons-site/fin_aid/migrations/0002_fin_aid_google_form_formfacade_code.py new file mode 100644 index 0000000..2b7de66 --- /dev/null +++ b/pycons-site/fin_aid/migrations/0002_fin_aid_google_form_formfacade_code.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2 on 2022-08-15 12:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fin_aid', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='fin_aid', + name='google_form_formfacade_code', + field=models.TextField(blank=True, default='', help_text='Link to your google form'), + ), + ] diff --git a/pycons-site/fin_aid/migrations/0003_alter_fin_aid_id.py b/pycons-site/fin_aid/migrations/0003_alter_fin_aid_id.py new file mode 100644 index 0000000..0139ad2 --- /dev/null +++ b/pycons-site/fin_aid/migrations/0003_alter_fin_aid_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-02-29 23:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fin_aid', '0002_fin_aid_google_form_formfacade_code'), + ] + + operations = [ + migrations.AlterField( + model_name='fin_aid', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/pycons-site/fin_aid/migrations/__init__.py b/pycons-site/fin_aid/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/fin_aid/mixins.py b/pycons-site/fin_aid/mixins.py new file mode 100644 index 0000000..50aac38 --- /dev/null +++ b/pycons-site/fin_aid/mixins.py @@ -0,0 +1,23 @@ +from django.core.exceptions import PermissionDenied + +class EditOwnFin_aidMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnFin_aidMixin, self).get_object(*args, **kwargs) + self.check_permission(obj) + return obj + def check_permission(self, obj): + if hasattr(obj, 'user'): + obj = obj.user + if obj == self.request.user: + return + else: + raise PermissionDenied() + +class EditOwnLoginMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnLoginMixin, self).get_object(*args, **kwargs) + if obj.email == self.request.user.email: + return obj + else: + raise PermissionDenied + diff --git a/pycons-site/fin_aid/models.py b/pycons-site/fin_aid/models.py new file mode 100644 index 0000000..7ea1588 --- /dev/null +++ b/pycons-site/fin_aid/models.py @@ -0,0 +1,49 @@ +from django.db import models +from django.urls import reverse +from django.conf import settings +from django.contrib.auth.models import User +from markdownx.models import MarkdownxField +from django.utils import timezone +from home.models import EventYear + +class Fin_aid(models.Model): + title = models.CharField(max_length=250, null=False, blank=False, help_text='Financial Assistance PyCon Africa') + financial_assistance = MarkdownxField(default='', help_text="[Supports Markdown] - Financial Assistance PyCon Africa.", null=False, blank=False) + google_form_formfacade_code = models.TextField(default='', help_text='Link to your google form', blank=True) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='fin_aid', default=User) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='fin_aids') + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + fin_open_date = models.DateTimeField(help_text='Date and time when the financial aid form opens', null=False, blank=False, default=timezone.now) + fin_close_date = models.DateTimeField(help_text='Date and time when the financial aid form closes', null=False, blank=False, default=timezone.now) + + class Meta: + permissions = [ + ("can_edit_fin_aid", "Can edit financial aid form"), + ] + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("fin_aid_detail", kwargs={"pk": self.pk}) + + def is_form_open(self): + now = timezone.now() + return self.fin_open_date <= now <= self.fin_close_date + + def is_form_closed(self): + now = timezone.now() + return now > self.fin_close_date + + def is_form_not_open_yet(self): + now = timezone.now() + return now < self.fin_open_date + + def get_form_status_message(self): + if self.is_form_not_open_yet(): + return "The financial aid application form will open on {}".format(self.fin_open_date.strftime("%Y-%m-%d %H:%M:%S")) + elif self.is_form_closed(): + return "The financial aid application form closed on {}".format(self.fin_close_date.strftime("%Y-%m-%d %H:%M:%S")) + else: + return "The financial aid application form is currently open." \ No newline at end of file diff --git a/pycons-site/fin_aid/templatetags/__init__.py b/pycons-site/fin_aid/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/fin_aid/templatetags/permissions.py b/pycons-site/fin_aid/templatetags/permissions.py new file mode 100644 index 0000000..44d00cf --- /dev/null +++ b/pycons-site/fin_aid/templatetags/permissions.py @@ -0,0 +1,7 @@ +from django import template + +register = template.Library() + +@register.filter(name='has_perm') +def has_perm(user, perm): + return user.has_perm(perm) diff --git a/pycons-site/fin_aid/tests.py b/pycons-site/fin_aid/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/fin_aid/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/fin_aid/urls.py b/pycons-site/fin_aid/urls.py new file mode 100644 index 0000000..2606b71 --- /dev/null +++ b/pycons-site/fin_aid/urls.py @@ -0,0 +1,9 @@ +from django.urls import path +from . import views + + +app_name = 'fin_aid' +urlpatterns = [ + path('', views.fin_aid, name='fin_aid'), + path('edit//', views.fin_aid_edit, name='fin_aid_edit'), +] \ No newline at end of file diff --git a/pycons-site/fin_aid/views.py b/pycons-site/fin_aid/views.py new file mode 100644 index 0000000..d5ba6e7 --- /dev/null +++ b/pycons-site/fin_aid/views.py @@ -0,0 +1,93 @@ +#home views + +# Core Django imports. +from django.shortcuts import render, get_object_or_404, redirect +from django.contrib import messages +from django.utils import timezone +from django.contrib.auth.decorators import login_required +from functools import reduce + + +# Included by me (3rd Parties and more) +from django.contrib.auth.models import User +from datetime import datetime +from django.views.generic.edit import UpdateView +from django.views.generic.base import TemplateView +from django.urls import reverse_lazy + +# Fin_aid application imports. +from .models import Fin_aid +from home.models import EventYear + +from django.contrib.auth.mixins import PermissionRequiredMixin +from .forms import Fin_aidForm + + +def fin_aid(request, year): + event_year = get_object_or_404(EventYear, year=year) + fin_aids = Fin_aid.objects.filter(event_year=event_year).order_by('-date_created') + for fin_aid in fin_aids: + fin_aid.is_open = fin_aid.is_form_open() + fin_aid.is_closed = fin_aid.is_form_closed() + fin_aid.not_open_yet = fin_aid.is_form_not_open_yet() + fin_aid.form_status_message = fin_aid.get_form_status_message() + return render(request, f'{year}/fin_aid/fin_aid.html', {'fin_aids': fin_aids, 'year': year}) + +@login_required +def fin_aid_edit(request, year, pk): + fin_aid = get_object_or_404(Fin_aid, pk=pk) + if not request.user.has_perm('fin_aid.can_edit_fin_aid'): + return redirect('fin_aid', year=year) + if request.method == "POST": + form = Fin_aidForm(request.POST, instance=fin_aid) + if form.is_valid(): + fin_aid = form.save(commit=False) + fin_aid.user = request.user + fin_aid.date_updated = timezone.now() + fin_aid.save() + return redirect('fin_aid', year=year) + else: + form = Fin_aidForm(instance=fin_aid) + return render(request, f'{year}/fin_aid/update_fin_aid.html', {'form': form, 'year': year}) + +class Fin_aidView(PermissionRequiredMixin, UpdateView): + form_class = Fin_aidForm + model = Fin_aid + template_name = "fin_aid/update_fin_aid.html" + permission_required = 'fin_aid.can_edit_fin_aid' + + def get_success_url(self): + return reverse_lazy('fin_aid', kwargs={'year': self.object.event_year.year}) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['title'] = 'Fin_aid' + context['year'] = timezone.now().year + context['fin_aid'] = self.object + context['is_open'] = self.object.is_form_open() + context['is_closed'] = self.object.is_form_closed() + context['not_open_yet'] = self.object.is_form_not_open_yet() + context['form_status_message'] = self.object.get_form_status_message() + return context + +@login_required +def fin_aid_update_view(request, year, id): + obj = get_object_or_404(Fin_aid, id=id) + if not request.user.has_perm('fin_aid.can_edit_fin_aid'): + return redirect('fin_aid', year=year) + if request.method == "POST": + form = Fin_aidForm(request.POST, instance=obj) + if form.is_valid(): + form.save() + return redirect('fin_aid', year=year) + else: + form = Fin_aidForm(instance=obj) + context = { + 'form': form, + 'is_open': obj.is_form_open(), + 'is_closed': obj.is_form_closed(), + 'not_open_yet': obj.is_form_not_open_yet(), + 'form_status_message': obj.get_form_status_message(), + 'year': year + } + return render(request, f'{year}/fin_aid/update_fin_aid.html', context) \ No newline at end of file diff --git a/pycons-site/health_safety_guideline/__init__.py b/pycons-site/health_safety_guideline/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/health_safety_guideline/admin.py b/pycons-site/health_safety_guideline/admin.py new file mode 100644 index 0000000..b95adf0 --- /dev/null +++ b/pycons-site/health_safety_guideline/admin.py @@ -0,0 +1,14 @@ +# Core Django imports. +from django.contrib import admin + +# Blog application imports. +from .models import Health_Safety_Guideline + +class Health_Safety_GuidelineAdmin(admin.ModelAdmin): + + list_display = ('title', 'date_created') + ordering = ['-date_created', ] + +# Registers the Health_Safety_Guidelinemodel at the admin backend. +admin.site.register(Health_Safety_Guideline, Health_Safety_GuidelineAdmin) + diff --git a/pycons-site/health_safety_guideline/apps.py b/pycons-site/health_safety_guideline/apps.py new file mode 100644 index 0000000..19f1857 --- /dev/null +++ b/pycons-site/health_safety_guideline/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class Health_Safety_GuidelineConfig(AppConfig): + name = 'health_safety_guideline' diff --git a/pycons-site/health_safety_guideline/forms.py b/pycons-site/health_safety_guideline/forms.py new file mode 100644 index 0000000..99fa600 --- /dev/null +++ b/pycons-site/health_safety_guideline/forms.py @@ -0,0 +1,43 @@ +""" +Forms and validation code for user registration. + +Note that all of these forms assume Django's bundle default ``User`` +model; since it's not possible for a form to anticipate in advance the +needs of custom user models, you will need to write your own forms if +you're using a custom model. + +""" +from django import forms + +from registration.users import UserModel + +# Third Parties +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Submit +from django_recaptcha.fields import ReCaptchaField + + +User = UserModel() + + +from .models import Health_Safety_Guideline + + + + +class Health_Safety_GuidelineForm(forms.ModelForm): + captcha = ReCaptchaField() + + class Meta: + model = Health_Safety_Guideline + fields = ('title', 'health_safety_guideline', 'user',) + + def __init__(self, *args, **kwargs): + super(Health_Safety_GuidelineForm, self).__init__(*args, **kwargs) + self.fields['user'].disabled = True + + self.helper = FormHelper() + self.helper.form_id = 'id-Crispy_Health_Safety_GuidelineForm' + self.helper.form_class = 'form-horizontal' + self.helper.add_input(Submit('update', 'Health_Safety_Guideline ')) + diff --git a/pycons-site/health_safety_guideline/migrations/0001_initial.py b/pycons-site/health_safety_guideline/migrations/0001_initial.py new file mode 100644 index 0000000..a141e17 --- /dev/null +++ b/pycons-site/health_safety_guideline/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 3.2 on 2022-08-23 02:31 + +from django.conf import settings +import django.contrib.auth.models +from django.db import migrations, models +import django.db.models.deletion +import markdownx.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Health_Safety_Guideline', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Code of Conduct PyCon Africa', max_length=250)), + ('health_safety_guideline', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - COC PyCon Africa.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(default=django.contrib.auth.models.User, on_delete=django.db.models.deletion.CASCADE, related_name='health_safety_guideline', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/pycons-site/health_safety_guideline/migrations/0002_alter_health_safety_guideline_id.py b/pycons-site/health_safety_guideline/migrations/0002_alter_health_safety_guideline_id.py new file mode 100644 index 0000000..9a8f32e --- /dev/null +++ b/pycons-site/health_safety_guideline/migrations/0002_alter_health_safety_guideline_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-02-29 23:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('health_safety_guideline', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='health_safety_guideline', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/pycons-site/health_safety_guideline/migrations/__init__.py b/pycons-site/health_safety_guideline/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/health_safety_guideline/mixins.py b/pycons-site/health_safety_guideline/mixins.py new file mode 100644 index 0000000..a566b51 --- /dev/null +++ b/pycons-site/health_safety_guideline/mixins.py @@ -0,0 +1,23 @@ +from django.core.exceptions import PermissionDenied + +class EditOwnHealth_Safety_GuidelineMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnHealth_Safety_GuidelineMixin, self).get_object(*args, **kwargs) + self.check_permission(obj) + return obj + def check_permission(self, obj): + if hasattr(obj, 'user'): + obj = obj.user + if obj == self.request.user: + return + else: + raise PermissionDenied() + +class EditOwnLoginMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnLoginMixin, self).get_object(*args, **kwargs) + if obj.email == self.request.user.email: + return obj + else: + raise PermissionDenied + diff --git a/pycons-site/health_safety_guideline/models.py b/pycons-site/health_safety_guideline/models.py new file mode 100644 index 0000000..06a381c --- /dev/null +++ b/pycons-site/health_safety_guideline/models.py @@ -0,0 +1,24 @@ +from django.db import models +from django.urls import reverse +from django.conf import settings +from django.contrib.auth.models import User +from markdownx.models import MarkdownxField +from django.utils import timezone +from home.models import EventYear + +class Health_Safety_Guideline(models.Model): + title = models.CharField(max_length=250, null=False, blank=False, help_text='Code of Conduct PyCon Africa') + health_safety_guideline = MarkdownxField(default='', help_text = "[Supports Markdown] - COC PyCon Africa.", null=False, blank=False + ) + user = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='health_safety_guideline',default=User) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='health_safety_guidelines') + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("health_safety_guideline") + \ No newline at end of file diff --git a/pycons-site/health_safety_guideline/tests.py b/pycons-site/health_safety_guideline/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/health_safety_guideline/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/health_safety_guideline/urls.py b/pycons-site/health_safety_guideline/urls.py new file mode 100644 index 0000000..d5db9a5 --- /dev/null +++ b/pycons-site/health_safety_guideline/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from . import views + +app_name = 'health_safety_guideline' +urlpatterns = [ + path('', views.health_safety_guideline, name='health_safety_guideline') +] \ No newline at end of file diff --git a/pycons-site/health_safety_guideline/views.py b/pycons-site/health_safety_guideline/views.py new file mode 100644 index 0000000..11fcad7 --- /dev/null +++ b/pycons-site/health_safety_guideline/views.py @@ -0,0 +1,86 @@ +#home views +import operator + +# Core Django imports. +from django.shortcuts import render, get_object_or_404, redirect +from django.contrib import messages +from django.db.models import Q +from django.template import RequestContext +from django.views.generic.detail import DetailView +from django.views.generic import ( + DetailView, + ListView, +) +from django.utils import timezone +from django.contrib.auth.decorators import login_required +from functools import reduce + +from home.models import EventYear + +# Included by me (3rd Parties and more) +from django.contrib.auth.models import User +from datetime import datetime +from django.contrib.auth import authenticate, login, get_user_model +from django.views.generic.edit import UpdateView +from django.views.generic.base import TemplateView +from django.urls import reverse_lazy +from django.views import generic +from django.http import HttpResponseRedirect + +# Health_Safety_Guideline application imports. +from .models import Health_Safety_Guideline +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger + +from health_safety_guideline.mixins import EditOwnHealth_Safety_GuidelineMixin, EditOwnLoginMixin +from .forms import Health_Safety_GuidelineForm + + +def health_safety_guideline(request, year): + # Ensure the event year exists and is valid + event_year = get_object_or_404(EventYear, year=year) + # Filter Health and Safety Guidelines based on the event year + health_safety_guidelines = Health_Safety_Guideline.objects.filter(event_year=event_year).order_by('-date_created') + # Render the template with Health and Safety Guidelines specific to the given year + return render(request, f'{year}/health_safety_guidelines/health_safety_guidelines.html', {'health_safety_guidelines': health_safety_guidelines}) + +def health_safety_guideline_edit(request, pk): + health_safety_guideline = get_object_or_404(Health_Safety_Guideline, pk=pk) + if request.method == "POST": + form = Health_Safety_GuidelineForm(request.POST, instance=health_safety_guideline) + if form.is_valid(): + health_safety_guideline = form.save(commit=False) + health_safety_guideline.user = request.user + health_safety_guideline.date_updated = timezone.now() + health_safety_guideline.save() + return redirect('health_safety_guideline:health_safety_guideline_home') + else: + form = Health_Safety_GuidelineForm(instance=health_safety_guideline) + return render(request, '2024/health_safety_guideline/update_health_safety_guideline.html', {'form': form}) + + +class Health_Safety_GuidelineView(EditOwnHealth_Safety_GuidelineMixin, UpdateView): + form_class = Health_Safety_GuidelineForm + model = Health_Safety_Guideline + template_name = "2024/health_safety_guideline/update_health_safety_guideline.html" + success_url = reverse_lazy('health_safety_guideline:health_safety_guideline_home') + + def get_context_data(self, **kwargs): + context = super(UpdateView, self).get_context_data(**kwargs) + context['title'] = 'Health_Safety_Guideline' + context['year'] = datetime.now().year + try: + context['health_safety_guideline'] = Health_Safety_Guideline() + except Health_Safety_Guideline.DoesNotExist: + context['health_safety_guideline'] = '' + return context + +def health_safety_guideline_update_view(UpdateView): + obj = get_object_or_404(Health_Safety_Guideline, id=id) + form = Health_Safety_GuidelineForm(UpdateView.POST or None, instance=obj) + if form.is_valid(): + form.save() + context = { + 'form': form + } + return render(UpdateView, "2024/health_safety_guideline/update_health_safety_guideline.html", context) + diff --git a/pycons-site/home/__init__.py b/pycons-site/home/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/home/admin.py b/pycons-site/home/admin.py new file mode 100644 index 0000000..a8e13ad --- /dev/null +++ b/pycons-site/home/admin.py @@ -0,0 +1,17 @@ +from django.contrib import admin +from .models import * +from django.conf import settings +from django.db import models + +# Register your models here. +@admin.register(EventYear) +class EventYearAdmin(admin.ModelAdmin): + list_display = ('year', 'home_info_shortened') + search_fields = ('year', 'home_info') + + def home_info_shortened(self, obj): + """ + Shorten the 'home_info' content for a cleaner display in the admin list view. + """ + return (obj.home_info[:75] + '...') if len(obj.home_info) > 75 else obj.home_info + home_info_shortened.short_description = 'Home Info' diff --git a/pycons-site/home/apps.py b/pycons-site/home/apps.py new file mode 100644 index 0000000..90dc713 --- /dev/null +++ b/pycons-site/home/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class HomeConfig(AppConfig): + name = 'home' diff --git a/pycons-site/home/forms.py b/pycons-site/home/forms.py new file mode 100644 index 0000000..9ab587d --- /dev/null +++ b/pycons-site/home/forms.py @@ -0,0 +1,2 @@ +from django import forms +from .models import * \ No newline at end of file diff --git a/pycons-site/home/migrations/0001_initial.py b/pycons-site/home/migrations/0001_initial.py new file mode 100644 index 0000000..dc8c570 --- /dev/null +++ b/pycons-site/home/migrations/0001_initial.py @@ -0,0 +1,43 @@ +# Generated by Django 2.2.11 on 2020-06-30 10:41 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import django_extensions.db.fields +import imagekit.models.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='KeynoteSpeaker', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('speaker_name', models.CharField(default='', max_length=200)), + ('profile_image', imagekit.models.fields.ProcessedImageField(default='speakers/speaker.png', upload_to='speakers/')), + ('profession', models.CharField(blank=True, default='', help_text="Speaker's profession. eg. Software Developer", max_length=200, null=True)), + ('organization', models.CharField(blank=True, default='', help_text="Speaker's Organization. eg. Google", max_length=200, null=True)), + ('country', models.CharField(blank=True, default='', help_text='City and Country the speaker is from eg. Accra, Ghana', max_length=100, null=True)), + ('biography', models.TextField(default='', help_text='The bio of the speaker', null=True)), + ('twitter', models.CharField(blank=True, default='', help_text="Please enter only the user name eg.'mawy_7' ", max_length=100, null=True)), + ('github', models.CharField(blank=True, default='', help_text="Please enter only the user name eg.'mawy_7' ", max_length=100, null=True)), + ('linkedin', models.CharField(blank=True, default='', help_text="Please enter only the user name eg.'/mawy_7' ", max_length=100, null=True)), + ('youtube_vide_url', models.URLField(blank=True, default='', help_text='Link to Talk on youtube Video')), + ('youtube_iframe_url', models.URLField(blank=True, default='', help_text='Link to Youtube Iframe', max_length=300)), + ('created_date', models.DateTimeField(default=django.utils.timezone.now)), + ('is_visible', models.BooleanField(default=False)), + ('published_date', models.DateField(blank=True, null=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('slug', django_extensions.db.fields.AutoSlugField(blank=True, editable=False, populate_from='speaker_name')), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/pycons-site/home/migrations/0002_auto_20200630_1048.py b/pycons-site/home/migrations/0002_auto_20200630_1048.py new file mode 100644 index 0000000..5bf31e8 --- /dev/null +++ b/pycons-site/home/migrations/0002_auto_20200630_1048.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.11 on 2020-06-30 10:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='keynotespeaker', + name='organization', + field=models.CharField(blank=True, default='', help_text="KeynoteSpeaker's Organization. eg. Google", max_length=200, null=True), + ), + migrations.AlterField( + model_name='keynotespeaker', + name='profession', + field=models.CharField(blank=True, default='', help_text="KeynoteSpeaker's profession. eg. Software Developer", max_length=200, null=True), + ), + ] diff --git a/pycons-site/home/migrations/0003_auto_20200630_1235.py b/pycons-site/home/migrations/0003_auto_20200630_1235.py new file mode 100644 index 0000000..8b9b471 --- /dev/null +++ b/pycons-site/home/migrations/0003_auto_20200630_1235.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.11 on 2020-06-30 12:35 + +from django.db import migrations +import imagekit.models.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0002_auto_20200630_1048'), + ] + + operations = [ + migrations.AlterField( + model_name='keynotespeaker', + name='profile_image', + field=imagekit.models.fields.ProcessedImageField(default='keynotes/speaker.png', upload_to='keynotes/'), + ), + ] diff --git a/pycons-site/home/migrations/0004_auto_20200711_1650.py b/pycons-site/home/migrations/0004_auto_20200711_1650.py new file mode 100644 index 0000000..06e29c1 --- /dev/null +++ b/pycons-site/home/migrations/0004_auto_20200711_1650.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.11 on 2020-07-11 16:50 + +from django.db import migrations +import tinymce.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0003_auto_20200630_1235'), + ] + + operations = [ + migrations.AlterField( + model_name='keynotespeaker', + name='biography', + field=tinymce.models.HTMLField(default='', help_text='The bio of the speaker', null=True, verbose_name='Biography'), + ), + ] diff --git a/pycons-site/home/migrations/0005_auto_20240226_1144.py b/pycons-site/home/migrations/0005_auto_20240226_1144.py new file mode 100644 index 0000000..7749fca --- /dev/null +++ b/pycons-site/home/migrations/0005_auto_20240226_1144.py @@ -0,0 +1,24 @@ +# Generated by Django 2.2.11 on 2024-02-26 11:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0004_auto_20200711_1650'), + ] + + operations = [ + migrations.CreateModel( + name='EventYear', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('year', models.IntegerField(unique=True)), + ('home_info', models.TextField(blank=True, null=True)), + ], + ), + migrations.DeleteModel( + name='KeynoteSpeaker', + ), + ] diff --git a/pycons-site/home/migrations/0006_eventyear_template_name.py b/pycons-site/home/migrations/0006_eventyear_template_name.py new file mode 100644 index 0000000..a034798 --- /dev/null +++ b/pycons-site/home/migrations/0006_eventyear_template_name.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.11 on 2024-02-26 14:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0005_auto_20240226_1144'), + ] + + operations = [ + migrations.AddField( + model_name='eventyear', + name='template_name', + field=models.CharField(default='home/home.html', help_text="Enter the template file name for this event year. Example: 'events/2024_home.html'", max_length=255), + ), + ] diff --git a/pycons-site/home/migrations/0007_auto_20240226_1717.py b/pycons-site/home/migrations/0007_auto_20240226_1717.py new file mode 100644 index 0000000..7bafe3e --- /dev/null +++ b/pycons-site/home/migrations/0007_auto_20240226_1717.py @@ -0,0 +1,26 @@ +# Generated by Django 2.2.11 on 2024-02-26 17:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0006_eventyear_template_name'), + ] + + operations = [ + migrations.AlterModelOptions( + name='eventyear', + options={'verbose_name': 'Event Year', 'verbose_name_plural': 'Event Years'}, + ), + migrations.RemoveField( + model_name='eventyear', + name='template_name', + ), + migrations.AddField( + model_name='eventyear', + name='template_path', + field=models.CharField(default='home/home.html', help_text="Path to the year's templates, e.g., '2020/home/home.html'", max_length=255), + ), + ] diff --git a/pycons-site/home/migrations/0008_pyconevent.py b/pycons-site/home/migrations/0008_pyconevent.py new file mode 100644 index 0000000..62df472 --- /dev/null +++ b/pycons-site/home/migrations/0008_pyconevent.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.11 on 2024-02-28 09:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0007_auto_20240226_1717'), + ] + + operations = [ + migrations.CreateModel( + name='PyConEvent', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('country', models.CharField(max_length=255)), + ('flag_image', models.ImageField(default='flag.jpg', upload_to='countryflags/')), + ('city', models.CharField(blank=True, max_length=255)), + ('start_date', models.DateField()), + ('end_date', models.DateField()), + ('year', models.IntegerField()), + ('website_url', models.URLField(blank=True)), + ], + options={ + 'ordering': ['start_date'], + }, + ), + ] diff --git a/pycons-site/home/migrations/__init__.py b/pycons-site/home/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/home/models.py b/pycons-site/home/models.py new file mode 100644 index 0000000..972d534 --- /dev/null +++ b/pycons-site/home/models.py @@ -0,0 +1,52 @@ +from django.conf import settings +from django.db import models +from django.utils import timezone +from django.contrib.auth.models import User +import os +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType + +from django_extensions.db.fields import AutoSlugField +from django_slugify_processor.text import slugify +from django.contrib.contenttypes.fields import GenericRelation + +from six import python_2_unicode_compatible +from tinymce.models import HTMLField + +from imagekit.models import ProcessedImageField +from imagekit.processors import ResizeToFit +@python_2_unicode_compatible + + +class PyConEvent(models.Model): + name = models.CharField(max_length=255) + country = models.CharField(max_length=255) + flag_image = models.ImageField(upload_to='countryflags/',default="flag.jpg") + city = models.CharField(max_length=255, blank=True) + start_date = models.DateField() + end_date = models.DateField() + year = models.IntegerField() # Year of the Conference + website_url = models.URLField(blank=True) + + def __str__(self): + return f"{self.name} {self.year}" + + class Meta: + ordering = ['start_date'] + + +class EventYear(models.Model): + year = models.IntegerField(unique=True) + home_info = models.TextField(blank=True, null=True) + template_path = models.CharField(max_length=255, default="home/home.html", help_text="Path to the year's templates, e.g., '2020/home/home.html'") + + def __str__(self): + return f"PyCon Africa {self.year}" + + class Meta: + verbose_name = 'Event Year' + verbose_name_plural = 'Event Years' + + + def save(self,*args,**kwargs): + if settings.DEBUG == True: diff --git a/pycons-site/home/templatetags/__init__.py b/pycons-site/home/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/home/templatetags/base_extras.py b/pycons-site/home/templatetags/base_extras.py new file mode 100644 index 0000000..f83f9ca --- /dev/null +++ b/pycons-site/home/templatetags/base_extras.py @@ -0,0 +1,24 @@ +from django import template +from django.urls import reverse + +register = template.Library() + +@register.simple_tag +def navactive(request, urls): + if request.path in ( reverse(url) for url in urls.split() ): + return "active" + return "" + +@register.simple_tag +def time_of_day(): + import datetime, pytz + from django.conf import settings + cur_time = datetime.datetime.now(tz=pytz.timezone(str(settings.TIME_ZONE))) + if cur_time.hour < 12: + return 'Good Morning 🌅' + elif 12 <= cur_time.hour < 16: + return 'Good Afternoon 🌞' + else: + return 'Good Evening 🌙' + + \ No newline at end of file diff --git a/pycons-site/home/templatetags/current_year.py b/pycons-site/home/templatetags/current_year.py new file mode 100644 index 0000000..bced914 --- /dev/null +++ b/pycons-site/home/templatetags/current_year.py @@ -0,0 +1,8 @@ +from django import template +import datetime + +register = template.Library() + +@register.simple_tag +def current_year(): + return datetime.datetime.now().year \ No newline at end of file diff --git a/pycons-site/home/templatetags/keynotes_tag.py b/pycons-site/home/templatetags/keynotes_tag.py new file mode 100644 index 0000000..e042284 --- /dev/null +++ b/pycons-site/home/templatetags/keynotes_tag.py @@ -0,0 +1,63 @@ +import random +from django import template +from django.db.models import Q, Case, When, IntegerField, Exists, OuterRef +from registration.models import Profile +from talks.models import Proposal + +register = template.Library() + +@register.simple_tag +def get_speakers_for_homepage(): + queryset = Profile.objects.filter( + Q(user__proposals__status='A', user__proposals__user_response='A') | + Q(user__speaking_proposals__status='A', user__speaking_proposals__user_response='A') + ).annotate( + is_keynote_speaker=Exists( + Proposal.objects.filter( + user=OuterRef('user'), + talk_type="Keynote Speaker", + status='A', + user_response='A' + ) + ), + sort_priority=Case( + When(user__proposals__talk_type="Sponsored Talk", then=1), + When(user__proposals__talk_type__in=["Short Talk", "Long Talk"], then=2), + When(user__proposals__talk_type="Lightning Talk", then=3), + default=4, + output_field=IntegerField(), + ) + ).order_by('sort_priority', 'user', 'date_created') + + # Remove duplicate users + seen_users = set() + unique_speakers = [] + for speaker in queryset: + if speaker.user_id not in seen_users: + seen_users.add(speaker.user_id) + unique_speakers.append(speaker) + + # Separate keynote speakers + keynote_speakers = [speaker for speaker in unique_speakers if speaker.is_keynote_speaker] + + # Separate and prioritize Sponsored Talk speakers + sponsored_speakers = [speaker for speaker in unique_speakers if speaker.sort_priority == 1] + + # Get remaining speakers, excluding keynote and sponsored talk speakers + other_speakers = [speaker for speaker in unique_speakers if not speaker.is_keynote_speaker and speaker.sort_priority != 1] + + # Shuffle and select remaining speakers to fill up the 4-speaker limit + remaining_slots = max(0, 4 - len(sponsored_speakers)) + random.shuffle(other_speakers) + selected_other_speakers = sponsored_speakers + other_speakers[:remaining_slots] + + # Ensure we only return 4 final speakers, prioritizing sponsored talk + final_speakers = selected_other_speakers[:4] + + return { + 'keynote_speakers': keynote_speakers, + 'final_speakers': final_speakers, + } + + + \ No newline at end of file diff --git a/pycons-site/home/templatetags/media_tags.py b/pycons-site/home/templatetags/media_tags.py new file mode 100644 index 0000000..1bdd5fb --- /dev/null +++ b/pycons-site/home/templatetags/media_tags.py @@ -0,0 +1,8 @@ +from django import template +from django.conf import settings + +register = template.Library() + +@register.simple_tag +def media(file): + return f"{settings.MEDIA_URL}{file}" diff --git a/pycons-site/home/tests.py b/pycons-site/home/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/home/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/home/urls.py b/pycons-site/home/urls.py new file mode 100644 index 0000000..7831156 --- /dev/null +++ b/pycons-site/home/urls.py @@ -0,0 +1,14 @@ +from django.urls import path, include +from django.views.generic import TemplateView +from .views import * + +from . import views + + +app_name = 'home' +urlpatterns = [ + path('', homepage, name='homepage'), + path('/', views.home_for_year, name='home_for_year'), + path('hitcount/', include(('hitcount.urls', 'hitcount'), namespace='hitcount')), +] + diff --git a/pycons-site/home/views.py b/pycons-site/home/views.py new file mode 100644 index 0000000..8fb1243 --- /dev/null +++ b/pycons-site/home/views.py @@ -0,0 +1,55 @@ +from django.shortcuts import render, get_object_or_404 +from django.utils.timezone import now +from .models import * +from .models import EventYear +from fin_aid.models import Fin_aid + +def get_fin_aid_status(year): + event_year = get_object_or_404(EventYear, year=year) + fin_aids = Fin_aid.objects.filter(event_year=event_year).order_by('-date_created') + fin_aid_status = [] + + for fin_aid in fin_aids: + status = { + 'is_open': fin_aid.is_form_open(), + 'is_closed': fin_aid.is_form_closed(), + 'not_open_yet': fin_aid.is_form_not_open_yet(), + 'form_status_message': fin_aid.get_form_status_message() + } + fin_aid_status.append(status) + + return fin_aid_status + +def homepage(request): + current_year = now().year + upcoming_pycon_events = PyConEvent.objects.filter(start_date__gte=now()).exclude(year=current_year) + pycon_africa_this_year = EventYear.objects.filter(year=current_year).first() + fin_aid_status = get_fin_aid_status(current_year) + + context = { + 'upcoming_pycon_events': upcoming_pycon_events, + 'pycon_africa_this_year': pycon_africa_this_year, + 'current_year': current_year, + 'fin_aid_status': fin_aid_status, + } + + return render(request, 'home/home.html', context) + + +def home_for_year(request, year): + event_year = EventYear.objects.get(year=year) + template_name = f"{event_year.template_path}" + context = { + # Your context data here + 'event_year': event_year, + } + return render(request, template_name, context) + + + + + +def handler404(request, exception): + return render(request, '404.html', locals()) + + diff --git a/pycons-site/management/__init__.py b/pycons-site/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/management/commands/__init__.py b/pycons-site/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/management/commands/copy_files.py b/pycons-site/management/commands/copy_files.py new file mode 100644 index 0000000..1205e4e --- /dev/null +++ b/pycons-site/management/commands/copy_files.py @@ -0,0 +1,57 @@ +import os +import shutil +from django.conf import settings +from django.core.management.base import BaseCommand +from django.utils.timezone import now + + +class Command(BaseCommand): + help = 'Copies templates and static files from the package into the project.' + + def add_arguments(self, parser): + # Add event_year as an argument to be passed by the user + parser.add_argument( + '--event-year', type=str, required=True, help='Specify the event year for the template folder.' + ) + + def handle(self, *args, **kwargs): + event_year = kwargs['event_year'] + + # Get the paths for the templates and static directories in the Django project + templates_dir = settings.TEMPLATES[0]['DIRS'][0] + static_dir = settings.STATICFILES_DIRS[0] + + # Path for the new event year folder inside templates + event_year_folder = os.path.join(templates_dir, f"event_{event_year}") + os.makedirs(event_year_folder, exist_ok=True) + + # Copy templates from the package to the event year folder + package_templates_dir = os.path.join(os.path.dirname(__file__), 'templates') + self.copy_files(package_templates_dir, event_year_folder) + + # Copy static files from the package to the static directory + package_static_dir = os.path.join(os.path.dirname(__file__), 'static') # Your package's static directory + self.copy_files(package_static_dir, static_dir) + + self.stdout.write(self.style.SUCCESS(f'Successfully copied files for event year {event_year}')) + + def copy_files(self, src_dir, dest_dir): + """Helper function to copy files from source to destination.""" + for root, dirs, files in os.walk(src_dir): + # Get relative path from the source directory + relative_path = os.path.relpath(root, src_dir) + destination_dir = os.path.join(dest_dir, relative_path) + + # Ensure destination directory exists + os.makedirs(destination_dir, exist_ok=True) + + for file_name in files: + # For template files, rename them to default-{filename} + if src_dir.endswith('templates'): + file_name = f'default-{file_name}' + # Copy the file + src_file = os.path.join(root, file_name) + dest_file = os.path.join(destination_dir, file_name) + + # Copy the file + shutil.copy(src_file, dest_file) diff --git a/pycons-site/privacypolicy/__init__.py b/pycons-site/privacypolicy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/privacypolicy/admin.py b/pycons-site/privacypolicy/admin.py new file mode 100644 index 0000000..3de47c5 --- /dev/null +++ b/pycons-site/privacypolicy/admin.py @@ -0,0 +1,14 @@ +# Core Django imports. +from django.contrib import admin + +# Blog application imports. +from .models import PrivacyPolicy + +class PrivacyPolicyAdmin(admin.ModelAdmin): + + list_display = ('title', 'date_created') + ordering = ['-date_created', ] + +# Registers the PrivacyPolicymodel at the admin backend. +admin.site.register(PrivacyPolicy, PrivacyPolicyAdmin) + diff --git a/pycons-site/privacypolicy/apps.py b/pycons-site/privacypolicy/apps.py new file mode 100644 index 0000000..2ab5d9f --- /dev/null +++ b/pycons-site/privacypolicy/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class PrivacypolicyConfig(AppConfig): + name = 'privacypolicy' diff --git a/pycons-site/privacypolicy/migrations/0001_initial.py b/pycons-site/privacypolicy/migrations/0001_initial.py new file mode 100644 index 0000000..2616788 --- /dev/null +++ b/pycons-site/privacypolicy/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 3.2 on 2022-03-30 12:28 + +from django.conf import settings +import django.contrib.auth.models +from django.db import migrations, models +import django.db.models.deletion +import markdownx.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Privacy', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Privacy policies of PyCon Africa', max_length=250)), + ('privacy_policy', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - COC PyCon Africa.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(default=django.contrib.auth.models.User, on_delete=django.db.models.deletion.CASCADE, related_name='privacy', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/pycons-site/privacypolicy/migrations/0002_auto_20220330_1232.py b/pycons-site/privacypolicy/migrations/0002_auto_20220330_1232.py new file mode 100644 index 0000000..35abc97 --- /dev/null +++ b/pycons-site/privacypolicy/migrations/0002_auto_20220330_1232.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2 on 2022-03-30 12:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('privacypolicy', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='privacy', + options={'verbose_name': 'privacy', 'verbose_name_plural': 'privacies'}, + ), + migrations.AlterUniqueTogether( + name='privacy', + unique_together={('title',)}, + ), + ] diff --git a/pycons-site/privacypolicy/migrations/0003_auto_20220330_1245.py b/pycons-site/privacypolicy/migrations/0003_auto_20220330_1245.py new file mode 100644 index 0000000..e9c4ff6 --- /dev/null +++ b/pycons-site/privacypolicy/migrations/0003_auto_20220330_1245.py @@ -0,0 +1,37 @@ +# Generated by Django 3.2 on 2022-03-30 12:45 + +from django.conf import settings +import django.contrib.auth.models +from django.db import migrations, models +import django.db.models.deletion +import markdownx.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('privacypolicy', '0002_auto_20220330_1232'), + ] + + operations = [ + migrations.CreateModel( + name='PrivacyPolicy', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Privacy policies of PyCon Africa', max_length=250)), + ('privacypolicy_policy', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - COC PyCon Africa.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(default=django.contrib.auth.models.User, on_delete=django.db.models.deletion.CASCADE, related_name='privacypolicy', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'privacypolicy', + 'verbose_name_plural': 'privacypolicies', + 'unique_together': {('title',)}, + }, + ), + migrations.DeleteModel( + name='Privacy', + ), + ] diff --git a/pycons-site/privacypolicy/migrations/0004_rename_privacypolicy_policy_privacypolicy_privacy_policy.py b/pycons-site/privacypolicy/migrations/0004_rename_privacypolicy_policy_privacypolicy_privacy_policy.py new file mode 100644 index 0000000..562baa5 --- /dev/null +++ b/pycons-site/privacypolicy/migrations/0004_rename_privacypolicy_policy_privacypolicy_privacy_policy.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2 on 2022-03-30 12:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('privacypolicy', '0003_auto_20220330_1245'), + ] + + operations = [ + migrations.RenameField( + model_name='privacypolicy', + old_name='privacypolicy_policy', + new_name='privacy_policy', + ), + ] diff --git a/pycons-site/privacypolicy/migrations/0005_alter_privacypolicy_id.py b/pycons-site/privacypolicy/migrations/0005_alter_privacypolicy_id.py new file mode 100644 index 0000000..5c06372 --- /dev/null +++ b/pycons-site/privacypolicy/migrations/0005_alter_privacypolicy_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-02-29 23:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('privacypolicy', '0004_rename_privacypolicy_policy_privacypolicy_privacy_policy'), + ] + + operations = [ + migrations.AlterField( + model_name='privacypolicy', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/pycons-site/privacypolicy/migrations/__init__.py b/pycons-site/privacypolicy/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/privacypolicy/models.py b/pycons-site/privacypolicy/models.py new file mode 100644 index 0000000..761403c --- /dev/null +++ b/pycons-site/privacypolicy/models.py @@ -0,0 +1,31 @@ +from django.db import models +from django.urls import reverse +from django.conf import settings +from django.contrib.auth.models import User +from markdownx.models import MarkdownxField +from django.utils import timezone +from home.models import EventYear + +class PrivacyPolicy(models.Model): + title = models.CharField(max_length=250, null=False, blank=False, help_text='Privacy policies of PyCon Africa') + privacy_policy = MarkdownxField(default='', help_text = "[Supports Markdown] - COC PyCon Africa.", null=False, blank=False + ) + user = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='privacypolicy',default=User) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='privacypolicies') + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + + class Meta: + unique_together = ('title',) + verbose_name = 'privacypolicy' + verbose_name_plural = 'privacypolicies' + + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("privacypolicies") + \ No newline at end of file diff --git a/pycons-site/privacypolicy/tests.py b/pycons-site/privacypolicy/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/privacypolicy/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/privacypolicy/urls.py b/pycons-site/privacypolicy/urls.py new file mode 100644 index 0000000..36f0c6b --- /dev/null +++ b/pycons-site/privacypolicy/urls.py @@ -0,0 +1,9 @@ +from django.conf.urls.static import static +from django.contrib import admin +from django.urls import include, path +from . import views + +app_name = 'privacypolicy' +urlpatterns = [ + path('', view=views.privacypolicy, name='privacypolicy'), +] diff --git a/pycons-site/privacypolicy/views.py b/pycons-site/privacypolicy/views.py new file mode 100644 index 0000000..c55a2da --- /dev/null +++ b/pycons-site/privacypolicy/views.py @@ -0,0 +1,29 @@ +#home views +import operator + +# Core Django imports. +from django.shortcuts import render, get_object_or_404, redirect +from django.contrib import messages +from django.db.models import Q +from django.template import RequestContext +from django.views.generic.detail import DetailView +from django.views.generic import ( + DetailView, + ListView, +) +from django.utils import timezone +from django.contrib.auth.decorators import login_required +from functools import reduce +from home.models import EventYear + +# PrivacyPolicy application imports. +from .models import PrivacyPolicy +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger + +def privacypolicy(request, year): + # Ensure the event year exists and is valid + event_year = get_object_or_404(EventYear, year=year) + # Filter Privacy Policies based on the event year + privacypolicies = PrivacyPolicy.objects.filter(event_year=event_year).order_by('-date_created') + # Render the template with Privacy Policies specific to the given year + return render(request, f'{year}/privacy/privacy.html', {'privacypolicies': privacypolicies}) \ No newline at end of file diff --git a/pycons-site/registration/__init__.py b/pycons-site/registration/__init__.py new file mode 100644 index 0000000..24b21e0 --- /dev/null +++ b/pycons-site/registration/__init__.py @@ -0,0 +1,22 @@ +VERSION = (2, 1, 0, 'final', 0) + + +def get_version(): + "Returns a PEP 386-compliant version number from VERSION." + assert len(VERSION) == 5 + assert VERSION[3] in ('alpha', 'beta', 'rc', 'final') + + # Now build the two parts of the version number: + # main = X.Y[.Z] + # sub = .devN - for pre-alpha releases + # | {a|b|c}N - for alpha, beta and rc releases + + parts = 2 if VERSION[2] == 0 else 3 + main = '.'.join(str(x) for x in VERSION[:parts]) + + sub = '' + if VERSION[3] != 'final': + mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'} + sub = mapping[VERSION[3]] + str(VERSION[4]) + + return str(main + sub) diff --git a/pycons-site/registration/admin.py b/pycons-site/registration/admin.py new file mode 100644 index 0000000..8b5cbfd --- /dev/null +++ b/pycons-site/registration/admin.py @@ -0,0 +1,77 @@ +from django.contrib import admin +from django.contrib.sites.shortcuts import get_current_site + +from markdownx.admin import MarkdownxModelAdmin +from .models import RegistrationProfile, Profile +from .users import UsernameField +from .utils import _ +from django.utils.html import format_html + + +class SpeakerInline(admin.StackedInline): + model = Profile + extra = 0 + + +class RegistrationAdmin(admin.ModelAdmin): + actions = ['activate_users', 'resend_activation_email'] + list_display = ('user', 'activation_key_expired') + raw_id_fields = ['user'] + search_fields = ('user__{0}'.format(UsernameField()), + 'user__first_name', 'user__last_name') + + def activate_users(self, request, queryset): + """ + Activates the selected users, if they are not already + activated. + + """ + + site = get_current_site(request) + for profile in queryset: + RegistrationProfile.objects.activate_user(profile.activation_key, site) + activate_users.short_description = _("Activate users") + + def resend_activation_email(self, request, queryset): + """ + Re-sends activation emails for the selected users. + + Note that this will *only* send activation emails for users + who are eligible to activate; emails will not be sent to users + whose activation keys have expired or who have already + activated. + + """ + + site = get_current_site(request) + for profile in queryset: + user = profile.user + RegistrationProfile.objects.resend_activation_mail(user.email, site, request) + + resend_activation_email.short_description = _("Re-send activation emails") + + +admin.site.register(RegistrationProfile, RegistrationAdmin) + + +class PersonalAdmin(admin.ModelAdmin): + list_display = ( + "profile_picture_thumbnail", "name", "surname", "user", "is_visible", + 'is_a_sponsor_or_keynote_speaker', 'profession', 'organization', + 'contact_number', 'city', 'country', 'date_created', 'updated' + ) + list_editable = ["is_visible", 'is_a_sponsor_or_keynote_speaker'] + ordering = ['-date_created'] + + def profile_picture_thumbnail(self, obj): + if obj.profile_image: + return format_html( + '', + obj.profile_image.url + ) + return "No Image" + + profile_picture_thumbnail.short_description = 'Profile Picture' + +admin.site.register(Profile, PersonalAdmin) + \ No newline at end of file diff --git a/pycons-site/registration/api/__init__.py b/pycons-site/registration/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/registration/api/serializers.py b/pycons-site/registration/api/serializers.py new file mode 100644 index 0000000..4438f5c --- /dev/null +++ b/pycons-site/registration/api/serializers.py @@ -0,0 +1,31 @@ +from django.contrib.auth.models import User +from rest_framework import serializers + +from registration.models import Profile + + +class UserProfileSerializer(serializers.ModelSerializer): + class Meta: + model = Profile + fields = ('name', 'last_name', 'image' ) + + +class UserSerializer(serializers.HyperlinkedModelSerializer): + url = serializers.HyperlinkedIdentityField(view_name='user-detail', lookup_field='username') + + post_set = serializers.HyperlinkedRelatedField(view_name='post-detail', read_only=True, many=True) + profile = UserProfileSerializer() + + class Meta: + model = User + fields = ( + 'url', + 'id', + 'username', + 'name', + 'last_name', + 'post_set', + 'profile' + ) + + lookup_field = 'username' diff --git a/pycons-site/registration/api/urls.py b/pycons-site/registration/api/urls.py new file mode 100644 index 0000000..3e66f22 --- /dev/null +++ b/pycons-site/registration/api/urls.py @@ -0,0 +1,11 @@ +from django.urls import path, re_path +from rest_framework.urlpatterns import format_suffix_patterns + +from registration.api import views + +urlpatterns = [ + path('users/', views.user_list, name='user-list'), + re_path(r'^users/(?P[\w]+)$', views.user_detail, name='user-detail'), +] + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/pycons-site/registration/api/views.py b/pycons-site/registration/api/views.py new file mode 100644 index 0000000..163bbb3 --- /dev/null +++ b/pycons-site/registration/api/views.py @@ -0,0 +1,24 @@ +from rest_framework import status +from rest_framework.decorators import api_view +from rest_framework.response import Response +from django.contrib.auth.models import User + +from registration.api.serializers import UserSerializer + + +@api_view(['GET']) +def user_list(request): + users = User.objects.all() + serializer = UserSerializer(users, many=True, context={'request': request}) + return Response(serializer.data) + + +@api_view(['GET']) +def user_detail(request, username): + try: + user = User.objects.get(username=username) + except User.DoesNotExist: + return Response(status=status.HTTP_404_NOT_FOUND) + + serializer = UserSerializer(user, context={'request': request}) + return Response(serializer.data) diff --git a/pycons-site/registration/apps.py b/pycons-site/registration/apps.py new file mode 100644 index 0000000..d579a40 --- /dev/null +++ b/pycons-site/registration/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class RegistrationConfig(AppConfig): + name = 'registration' + verbose_name = "Registration" \ No newline at end of file diff --git a/pycons-site/registration/auth_urls.py b/pycons-site/registration/auth_urls.py new file mode 100644 index 0000000..bc1104c --- /dev/null +++ b/pycons-site/registration/auth_urls.py @@ -0,0 +1,71 @@ +""" +URL patterns for the views included in ``django.contrib.auth``. + +Including these URLs (via the ``include()`` directive) will set up the +following patterns based at whatever URL prefix they are included +under: + +* User login at ``login/``. + +* User logout at ``logout/``. + +* The two-step password change at ``password/change/`` and + ``password/change/done/``. + +* The four-step password reset at ``password/reset/``, + ``password/reset/confirm/``, ``password/reset/complete/`` and + ``password/reset/done/``. + +The default registration backend already has an ``include()`` for +these URLs, so under the default setup it is not necessary to manually +include these views. Other backends may or may not include them; +consult a specific backend's documentation for details. + +""" +from django.contrib.auth import views as auth_views +from django.urls import path +from django.urls import reverse_lazy + + + +from django.contrib.auth.decorators import login_required + +from .views import UpdateProfileView, ProfileView, UpdateLoginView, CreateProfileView, PasswordView, SuccessView + + +urlpatterns = [ + path('login/', + auth_views.LoginView.as_view( + template_name='registration/login.html'), + name='auth_login'), + path('logout/', + auth_views.LogoutView.as_view( + template_name='registration/logout.html'), + name='auth_logout'), + path('password/change/', + auth_views.PasswordChangeView.as_view( + success_url=reverse_lazy('auth_password_change_done')), + name='auth_password_change'), + path('password/change/done/', + auth_views.PasswordChangeDoneView.as_view(), + name='auth_password_change_done'), + path( + 'password/reset/', + auth_views.PasswordResetView.as_view( + success_url=reverse_lazy('auth_password_reset_done'), + html_email_template_name='registration/password_reset_email.html' + ), + name='auth_password_reset', +), + path('password/reset/complete/', + auth_views.PasswordResetCompleteView.as_view(), + name='auth_password_reset_complete'), + path('password/reset/done/', + auth_views.PasswordResetDoneView.as_view(), + name='auth_password_reset_done'), + path('password/reset/confirm///', + auth_views.PasswordResetConfirmView.as_view( + success_url=reverse_lazy('auth_password_reset_complete')), + name='auth_password_reset_confirm'), + path('profile/', login_required(ProfileView.as_view()), name='profile_home'), +] diff --git a/pycons-site/registration/backends/__init__.py b/pycons-site/registration/backends/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/registration/backends/admin_approval/__init__.py b/pycons-site/registration/backends/admin_approval/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/registration/backends/admin_approval/urls.py b/pycons-site/registration/backends/admin_approval/urls.py new file mode 100644 index 0000000..12d3c3f --- /dev/null +++ b/pycons-site/registration/backends/admin_approval/urls.py @@ -0,0 +1,77 @@ +""" +URLconf for registration and activation, using django-registration's +admin approval backend. + +If the default behavior of these views is acceptable to you, simply +use a line like this in your root URLconf to set up the default URLs +for profiles:: + + (r'^accounts/', include('registration.backends.admin_approval.urls')), + +This will also automatically set up the views in +``django.contrib.auth`` at sensible default locations. + +If you'd like to customize registration behavior, feel free to set up +your own URL patterns for these views instead. + +""" + + +from django.conf import settings +from django.conf.urls import include +from django.contrib.auth.decorators import permission_required +from django.urls import path +from django.views.generic.base import TemplateView + +from .views import ActivationView +from .views import ApprovalView +from .views import RegistrationView + +from registration.backends.admin_approval.views import ResendActivationView + +urlpatterns = [ + path('activate/resend/', + ResendActivationView.as_view(), + name='registration_resend_activation'), + path('activate/complete/', + TemplateView.as_view( + template_name='registration/activation_complete_admin_pending.html' + ), + name='registration_activation_complete'), + # Activation keys get matched by \w+ instead of the more specific + # [a-fA-F0-9]{40} because a bad activation key should still get to the view; + # that way it can return a sensible "invalid key" message instead of a + # confusing 404. + + path('activate//', + ActivationView.as_view(), + name='registration_activate'), + path('approve/complete/', + TemplateView.as_view( + template_name='registration/admin_approve_complete.html'), + name='registration_approve_complete'), + path('approve//', + permission_required('is_superuser')(ApprovalView.as_view()), + name='registration_admin_approve'), + path('register/complete/', + TemplateView.as_view( + template_name='registration/registration_complete.html'), + name='registration_complete'), + path('register/closed/', + TemplateView.as_view( + template_name='registration/registration_closed.html'), + name='registration_disallowed'), +] + + +if getattr(settings, 'INCLUDE_REGISTER_URL', True): + urlpatterns += [ + path('register/', + RegistrationView.as_view(), + name='registration_register'), + ] + +if getattr(settings, 'INCLUDE_AUTH_URLS', True): + urlpatterns += [ + path('', include('registration.auth_urls')), + ] diff --git a/pycons-site/registration/backends/admin_approval/views.py b/pycons-site/registration/backends/admin_approval/views.py new file mode 100644 index 0000000..7539e44 --- /dev/null +++ b/pycons-site/registration/backends/admin_approval/views.py @@ -0,0 +1,72 @@ +from django.contrib.sites.shortcuts import get_current_site + +from ... import signals +from ...models import SupervisedRegistrationProfile +from ...views import ApprovalView as BaseApprovalView +from ..default.views import ActivationView as BaseActivationView +from ..default.views import RegistrationView as BaseRegistrationView +from ..default.views import ResendActivationView as BaseResendActivationView + + +class RegistrationView(BaseRegistrationView): + + """ + Follows the exact logic of + ``registration.backends.default.views.RegistrationView`` but uses + ``SupervisedRegistrationProfile`` instead of ``RegistrationProfile`` + + """ + + registration_profile = SupervisedRegistrationProfile + + +class ActivationView(BaseActivationView): + + """ + Follows the exact logic of + ``registration.backends.default.views.ActivationView`` but uses + ``SupervisedRegistrationProfile`` instead of ``RegistrationProfile`` + + """ + + registration_profile = SupervisedRegistrationProfile + + +class ResendActivationView(BaseResendActivationView): + + """ + Follows the exact logic of + ``registration.backends.default.views.ResendActivationView`` but uses + ``SupervisedRegistrationProfile`` instead of ``RegistrationProfile`` + + """ + + registration_profile = SupervisedRegistrationProfile + + +class ApprovalView(BaseApprovalView): + def approve(self, *args, **kwargs): + """ + Given a user id, look up and approve the user account + corresponding to that key (if possible). + + After successful approval, the signal + ``registration.signals.user_approved`` will be sent, with the + newly approved ``User`` as the keyword argument ``user`` and + the class of this backend as the sender. + + """ + user_id = kwargs.get('profile_id', '') + approved_user = ( + SupervisedRegistrationProfile.objects.admin_approve_user( + user_id, get_current_site(self.request))) + if approved_user: + signals.user_approved.send( + sender=self.__class__, + user=approved_user, + request=self.request + ) + return approved_user + + def get_success_url(self, user): + return ('registration_approve_complete', (), {}) diff --git a/pycons-site/registration/backends/default/__init__.py b/pycons-site/registration/backends/default/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/registration/backends/default/urls.py b/pycons-site/registration/backends/default/urls.py new file mode 100644 index 0000000..b1f71c1 --- /dev/null +++ b/pycons-site/registration/backends/default/urls.py @@ -0,0 +1,61 @@ +""" +URLconf for registration and activation, using django-registration's +default backend. + +If the default behavior of these views is acceptable to you, simply +use a line like this in your root URLconf to set up the default URLs +for profiles:: + + (r'^accounts/', include('registration.backends.default.urls')), + +This will also automatically set up the views in +``django.contrib.auth`` at sensible default locations. + +If you'd like to customize registration behavior, feel free to set up +your own URL patterns for these views instead. + +""" + + +from django.conf import settings +from django.conf.urls import include +from django.urls import path +from django.views.generic.base import TemplateView + +from .views import ActivationView +from .views import RegistrationView +from .views import ResendActivationView + +urlpatterns = [ + path('activate/complete/', + TemplateView.as_view(template_name='registration/activation_complete.html'), + name='registration_activation_complete'), + path('activate/resend/', + ResendActivationView.as_view(), + name='registration_resend_activation'), + # Activation keys get matched by \w+ instead of the more specific + # [a-fA-F0-9]{40} because a bad activation key should still get to the view; + # that way it can return a sensible "invalid key" message instead of a + # confusing 404. + path('activate//', + ActivationView.as_view(), + name='registration_activate'), + path('signup/complete/', + TemplateView.as_view(template_name='registration/registration_complete.html'), + name='registration_complete'), + path('signup/closed/', + TemplateView.as_view(template_name='registration/registration_closed.html'), + name='registration_disallowed'), +] + +if getattr(settings, 'INCLUDE_REGISTER_URL', True): + urlpatterns += [ + path('signup/', + RegistrationView.as_view(), + name='registration_register'), + ] + +if getattr(settings, 'INCLUDE_AUTH_URLS', True): + urlpatterns += [ + path('', include('registration.auth_urls')), + ] diff --git a/pycons-site/registration/backends/default/views.py b/pycons-site/registration/backends/default/views.py new file mode 100644 index 0000000..195a683 --- /dev/null +++ b/pycons-site/registration/backends/default/views.py @@ -0,0 +1,180 @@ +from django.conf import settings +from django.contrib.sites.shortcuts import get_current_site +from django.shortcuts import render + +from ... import signals +from ...models import RegistrationProfile +from ...users import UserModel +from ...views import ActivationView as BaseActivationView +from ...views import RegistrationView as BaseRegistrationView +from ...views import ResendActivationView as BaseResendActivationView + + +class RegistrationView(BaseRegistrationView): + """ + A registration backend which follows a simple workflow: + + 1. User signs up, inactive account is created. + + 2. Email is sent to user with activation link. + + 3. User clicks activation link, account is now active. + + Using this backend requires that + + * ``registration`` be listed in the ``INSTALLED_APPS`` setting + (since this backend makes use of models defined in this + application). + + * The setting ``ACCOUNT_ACTIVATION_DAYS`` be supplied, specifying + (as an integer) the number of days from registration during + which a user may activate their account (after that period + expires, activation will be disallowed). + + * The creation of the templates + ``registration/activation_email_subject.txt`` and + ``registration/activation_email.txt``, which will be used for + the activation email. See the notes for this backends + ``register`` method for details regarding these templates. + + When subclassing this view, you can set the ``SEND_ACTIVATION_EMAIL`` + class variable to False to skip sending the new user a confirmation + email or set ``SEND_ACTIVATION_EMAIL`` to ``False``. Doing so implies + that you will have to activate the user manually from the admin site or + send an activation by some other method. For example, by listening for + the ``user_registered`` signal. + + Additionally, registration can be temporarily closed by adding the + setting ``REGISTRATION_OPEN`` and setting it to + ``False``. Omitting this setting, or setting it to ``True``, will + be interpreted as meaning that registration is currently open and + permitted. + + Internally, this is accomplished via storing an activation key in + an instance of ``registration.models.RegistrationProfile``. See + that model and its custom manager for full documentation of its + fields and supported operations. + + """ + SEND_ACTIVATION_EMAIL = getattr(settings, 'SEND_ACTIVATION_EMAIL', True) + success_url = 'registration_complete' + + registration_profile = RegistrationProfile + + def register(self, form): + """ + Given a username, email address and password, register a new + user account, which will initially be inactive. + + Along with the new ``User`` object, a new + ``registration.models.RegistrationProfile`` will be created, + tied to that ``User``, containing the activation key which + will be used for this account. + + An email will be sent to the supplied email address; this + email should contain an activation link. The email will be + rendered using two templates. See the documentation for + ``RegistrationProfile.send_activation_email()`` for + information about these templates and the contexts provided to + them. + + After the ``User`` and ``RegistrationProfile`` are created and + the activation email is sent, the signal + ``registration.signals.user_registered`` will be sent, with + the new ``User`` as the keyword argument ``user`` and the + class of this backend as the sender. + + """ + site = get_current_site(self.request) + + if hasattr(form, 'save'): + new_user_instance = form.save(commit=False) + else: + new_user_instance = (UserModel().objects + .create_user(**form.cleaned_data)) + + new_user = self.registration_profile.objects.create_inactive_user( + new_user=new_user_instance, + site=site, + send_email=self.SEND_ACTIVATION_EMAIL, + request=self.request, + ) + signals.user_registered.send(sender=self.__class__, + user=new_user, + request=self.request) + return new_user + + def registration_allowed(self): + """ + Indicate whether account registration is currently permitted, + based on the value of the setting ``REGISTRATION_OPEN``. This + is determined as follows: + + * If ``REGISTRATION_OPEN`` is not specified in settings, or is + set to ``True``, registration is permitted. + + * If ``REGISTRATION_OPEN`` is both specified and set to + ``False``, registration is not permitted. + + """ + return getattr(settings, 'REGISTRATION_OPEN', True) + + +class ActivationView(BaseActivationView): + + registration_profile = RegistrationProfile + + def activate(self, *args, **kwargs): + """ + Given an an activation key, look up and activate the user + account corresponding to that key (if possible). + + After successful activation, the signal + ``registration.signals.user_activated`` will be sent, with the + newly activated ``User`` as the keyword argument ``user`` and + the class of this backend as the sender. + + """ + activation_key = kwargs.get('activation_key', '') + site = get_current_site(self.request) + user, activated = self.registration_profile.objects.activate_user( + activation_key, site) + if activated: + signals.user_activated.send(sender=self.__class__, + user=user, + request=self.request) + return user + + def get_success_url(self, user): + return ('registration_activation_complete', (), {}) + + +class ResendActivationView(BaseResendActivationView): + + registration_profile = RegistrationProfile + + def resend_activation(self, form): + """ + Given an email, look up user by email and resend activation key + if user is not already activated or previous activation key has + not expired. Note that if multiple users exist with the given + email, no emails will be sent. + + Returns True if activation key was successfully sent, False otherwise. + + """ + site = get_current_site(self.request) + email = form.cleaned_data['email'] + return self.registration_profile.objects.resend_activation_mail( + email, site, self.request) + + def render_form_submitted_template(self, form): + """ + Renders resend activation complete template with the submitted email. + + """ + email = form.cleaned_data['email'] + context = {'email': email} + return render(self.request, + 'registration/resend_activation_complete.html', + context) diff --git a/pycons-site/registration/backends/simple/__init__.py b/pycons-site/registration/backends/simple/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/registration/backends/simple/urls.py b/pycons-site/registration/backends/simple/urls.py new file mode 100644 index 0000000..005e740 --- /dev/null +++ b/pycons-site/registration/backends/simple/urls.py @@ -0,0 +1,45 @@ +""" +URLconf for registration and activation, using django-registration's +one-step backend. + +If the default behavior of these views is acceptable to you, simply +use a line like this in your root URLconf to set up the default URLs +for profiles:: + + (r'^accounts/', include('registration.backends.simple.urls')), + +This will also automatically set up the views in +``django.contrib.auth`` at sensible default locations. + +If you'd like to customize registration behavior, feel free to set up +your own URL patterns for these views instead. + +""" + + +from django.conf import settings +from django.conf.urls import include +from django.urls import path +from django.views.generic.base import TemplateView + +from .views import RegistrationView + +urlpatterns = [ + path('register/closed/', + TemplateView.as_view(template_name='registration/registration_closed.html'), + name='registration_disallowed'), +] + +if getattr(settings, 'INCLUDE_REGISTER_URL', True): + urlpatterns += [ + path('register/', + RegistrationView.as_view( + success_url=getattr(settings, 'SIMPLE_BACKEND_REDIRECT_URL', '/'), + ), + name='registration_register'), + ] + +if getattr(settings, 'INCLUDE_AUTH_URLS', True): + urlpatterns += [ + path('', include('registration.auth_urls')), + ] diff --git a/pycons-site/registration/backends/simple/views.py b/pycons-site/registration/backends/simple/views.py new file mode 100644 index 0000000..ba5a142 --- /dev/null +++ b/pycons-site/registration/backends/simple/views.py @@ -0,0 +1,46 @@ +from django.conf import settings +from django.contrib.auth import authenticate +from django.contrib.auth import login + +from ... import signals +from ...views import RegistrationView as BaseRegistrationView + + +class RegistrationView(BaseRegistrationView): + """ + A registration backend which implements the simplest possible + workflow: a user supplies a username, email address and password + (the bare minimum for a useful account), and is immediately signed + up and logged in). + + """ + success_url = 'registration_complete' + + def register(self, form): + new_user = form.save() + username_field = getattr(new_user, 'USERNAME_FIELD', 'username') + new_user = authenticate( + username=getattr(new_user, username_field), + password=form.cleaned_data['password1'] + ) + + login(self.request, new_user) + signals.user_registered.send(sender=self.__class__, + user=new_user, + request=self.request) + return new_user + + def registration_allowed(self): + """ + Indicate whether account registration is currently permitted, + based on the value of the setting ``REGISTRATION_OPEN``. This + is determined as follows: + + * If ``REGISTRATION_OPEN`` is not specified in settings, or is + set to ``True``, registration is permitted. + + * If ``REGISTRATION_OPEN`` is both specified and set to + ``False``, registration is not permitted. + + """ + return getattr(settings, 'REGISTRATION_OPEN', True) diff --git a/pycons-site/registration/forms.py b/pycons-site/registration/forms.py new file mode 100644 index 0000000..3da6bd1 --- /dev/null +++ b/pycons-site/registration/forms.py @@ -0,0 +1,173 @@ +""" +Forms and validation code for user registration. + +Note that all of these forms assume Django's bundle default ``User`` +model; since it's not possible for a form to anticipate in advance the +needs of custom user models, you will need to write your own forms if +you're using a custom model. + +""" +from django import forms +from django.contrib.auth.forms import UserCreationForm, PasswordChangeForm, UserChangeForm + +from django.contrib.auth.models import User +from .users import UserModel +from .users import UsernameField +from .utils import _ +from django.utils.translation import gettext_lazy as _ + +# Third Parties +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Submit + +from django_countries.widgets import CountrySelectWidget +from django_recaptcha.fields import ReCaptchaField +from django_recaptcha.widgets import ReCaptchaV2Invisible + +User = UserModel() + + +from .models import Profile + +class RegistrationForm(UserCreationForm): + """ + Form for registering a new user account. + + Validates that the requested username is not already in use, and + requires the password to be entered twice to catch typos. + + Subclasses should feel free to add any additional validation they + need, but should avoid defining a ``save()`` method -- the actual + saving of collected user data is delegated to the active + registration backend. + + """ + required_css_class = 'required' + email = forms.EmailField(label=_("E-mail")) + captcha = ReCaptchaField() + + class Meta: + model = User + fields = (UsernameField(), "email") + + +class RegistrationFormUsernameLowercase(RegistrationForm): + """ + A subclass of :class:`RegistrationForm` which enforces unique case insensitive + usernames, make all usernames to lower case. + + """ + def clean_username(self): + username = self.cleaned_data.get('username', '').lower() + if User.objects.filter(**{UsernameField(): username}).exists(): + raise forms.ValidationError(_('A user with that username already exists.')) + + return username + + +class RegistrationFormTermsOfService(RegistrationForm): + """ + Subclass of ``RegistrationForm`` which adds a required checkbox + for agreeing to a site's Terms of Service. + + """ + tos = forms.BooleanField(widget=forms.CheckboxInput, + label=_('I have read and agree to the Terms of Service'), + error_messages={'required': _("You must agree to the terms to register")}) + + +class RegistrationFormUniqueEmail(RegistrationForm): + """ + Subclass of ``RegistrationForm`` which enforces uniqueness of + email addresses. + + """ + def clean_email(self): + """ + Validate that the supplied email address is unique for the + site. + + """ + if User.objects.filter(email__iexact=self.cleaned_data['email']): + raise forms.ValidationError(_("This email address is already in use. Please supply a different email address.")) + return self.cleaned_data['email'] + + +class RegistrationFormNoFreeEmail(RegistrationForm): + """ + Subclass of ``RegistrationForm`` which disallows registration with + email addresses from popular free webmail services; moderately + useful for preventing automated spam registrations. + + To change the list of banned domains, subclass this form and + override the attribute ``bad_domains``. + + """ + bad_domains = [ 'email.com', + ] + + def clean_email(self): + """ + Check the supplied email address against a list of known free + webmail domains. + + """ + email_domain = self.cleaned_data['email'].split('@')[1] + if email_domain in self.bad_domains: + raise forms.ValidationError(_("Registration using free email addresses is prohibited. Please supply a different email address.")) + return self.cleaned_data['email'] + + +class ResendActivationForm(forms.Form): + required_css_class = 'required' + email = forms.EmailField(label=_("E-mail")) + + +class UpdateForm(forms.ModelForm): + captcha = ReCaptchaField() + + class Meta: + model = Profile + fields = ('name', 'surname', 'profile_image', 'profession', 'organization', 'biography', 'twitter_handle', 'github_username', 'linkedin', 'contact_number', 'website', 'city', 'country',) + widgets = {'country': CountrySelectWidget()} + + def __init__(self, *args, **kwargs): + super(UpdateForm, self).__init__(*args, **kwargs) + + self.helper = FormHelper() + self.helper.form_id = 'id-Crispy_UpdateForm' + self.helper.form_class = 'form-horizontal' + self.helper.add_input(Submit('update', 'Update Profile')) + +class UserForm(forms.ModelForm): + captcha = ReCaptchaField() + + class Meta: + model = User + fields = ('first_name', 'last_name', 'email',) + + def __init__(self, *args, **kwargs): + super(UserForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_id = 'id-Crispy_UserForm' + self.helper.form_class = 'form-horizontal' + self.helper.add_input(Submit('update', 'Update Profile')) + + +class PasswordForm(PasswordChangeForm): + captcha = ReCaptchaField() + + def __init__(self, user, *args, **kwargs): + self.user = user + super(PasswordForm, self).__init__(*args, **kwargs) + + self.helper = FormHelper() + self.helper.form_id = 'id-Crispy_UserForm' + self.helper.form_class = 'form-horizontal' + self.helper.Layout( + 'old_password', + 'password1', + 'password2' + ) + self.helper.add_input(Submit('update', 'Update Profile')) + diff --git a/pycons-site/registration/locale/ar/LC_MESSAGES/django.po b/pycons-site/registration/locale/ar/LC_MESSAGES/django.po new file mode 100644 index 0000000..0b2f3b1 --- /dev/null +++ b/pycons-site/registration/locale/ar/LC_MESSAGES/django.po @@ -0,0 +1,445 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "رمز التفعيل" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "أقر بقراءة والموافقة على شروط الخدمة" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "يجب الموافقة على الشروط للتسجيل" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"عنوان البريد الالكتروني مسجل مسبقا. يرجى تزويد عنوان بريد الكتروني مختلف." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"يمنع التسجيل باستخدام عناوين بريد الكترونية مجانية. يرجى تزويد عنوان بريد " +"الكتروني مختلف." + +#: models.py:274 +msgid "user" +msgstr "مستخدم" + +#: models.py:276 +msgid "activation key" +msgstr "رمز التفعيل" + +#: models.py:282 +msgid "registration profile" +msgstr "ملف التسجيل الشخصي" + +#: models.py:283 +msgid "registration profiles" +msgstr "ملفات التسجيل الشخصية" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "رمز التفعيل" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "رمز التفعيل" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "ملف التسجيل الشخصي" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "رمز التفعيل" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "تأكيد كلمة المرور" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "كلمة المرور" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "كلمة المرور" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "كلمة المرور" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "كلمة المرور" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "ملف التسجيل الشخصي" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "رمز التفعيل" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "اسم المستخدم" + +#~ msgid "email address" +#~ msgstr "عنوان البريد الالكتروني" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "يمكن أن يحتوي اسم المستخدم على احرف، ارقام وشرطات سطرية فقط" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "اسم المستخدم مسجل مسبقا. يرجى اختيار اسم اخر." + +#~ msgid "You must type the same password each time" +#~ msgstr "يجب ادخال كلمة المرور مطابقة كل مرة" diff --git a/pycons-site/registration/locale/bg/LC_MESSAGES/django.po b/pycons-site/registration/locale/bg/LC_MESSAGES/django.po new file mode 100644 index 0000000..94f420d --- /dev/null +++ b/pycons-site/registration/locale/bg/LC_MESSAGES/django.po @@ -0,0 +1,444 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2008-03-05 12:37+0200\n" +"Last-Translator: Vladislav \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Bookmarks: -1,-1,-1,-1,10,-1,-1,-1,-1,-1\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "Ключ за активация" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Прочел съм и съм съгласен с условията за експлоатация" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Трябва да сте съгласни с условията за да се регистрирате." + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Адреса на електронната поща е използван. Моля въведете друг адрес." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Регистрациите с безплатни адреси е забранен. Моля въведете различен адрес за " +"електронна поща" + +#: models.py:274 +msgid "user" +msgstr "Потребител" + +#: models.py:276 +msgid "activation key" +msgstr "Ключ за активация" + +#: models.py:282 +msgid "registration profile" +msgstr "регистрационен профил" + +#: models.py:283 +msgid "registration profiles" +msgstr "регистрационни профили" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "Ключ за активация" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "Ключ за активация" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "регистрационен профил" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "Ключ за активация" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "Парола (проверка)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "Парола" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "Парола" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "Парола" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "Парола" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "регистрационен профил" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "Ключ за активация" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "Потребителско име " + +#~ msgid "email address" +#~ msgstr "Електронна поща" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "Потребителските имена могат да съдържат букви, цифри и подчертавки" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "Потребителското име е заето. Моля изберето друго." + +#~ msgid "You must type the same password each time" +#~ msgstr "Грешка при проверка на паролата." diff --git a/pycons-site/registration/locale/ca/LC_MESSAGES/django.po b/pycons-site/registration/locale/ca/LC_MESSAGES/django.po new file mode 100644 index 0000000..2f5f33f --- /dev/null +++ b/pycons-site/registration/locale/ca/LC_MESSAGES/django.po @@ -0,0 +1,443 @@ +# Catalan translation for django-registration. +# Copyright (C) 2007-2010, James Bennet +# This file is distributed under the same license as the django-registration package. +# Carles Barrobés , 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.8\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2010-09-24 23:21+0100\n" +"Last-Translator: Carles Barrobés i Meix \n" +"Language-Team: Català\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Catalan\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Activar usuaris" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Re-enviar e-mails d'activació" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "E-mail" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Ja existeix un usuari amb aquest nom" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "He llegit i estic d'acord amb les condicions d'ús" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Heu d'estar d'acord amb les condicions d'ús per registrar-vos" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Aquesta adreça d'e-mail ja està sent utilitzada. Sisplau, entreu-ne una " +"altra." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Està prohibit registrar-se utilitzant adreces d'e-mail gratuïtes. Sisplau " +"entreu-ne una altra." + +#: models.py:274 +msgid "user" +msgstr "usuari" + +#: models.py:276 +msgid "activation key" +msgstr "clau d'activació" + +#: models.py:282 +msgid "registration profile" +msgstr "perfil de registre" + +#: models.py:283 +msgid "registration profiles" +msgstr "perfils de registre" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "clau d'activació" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Account activation failed." +msgstr "Re-enviar e-mails d'activació" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "perfil de registre" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "clau d'activació" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "Password (again)" +msgid "Password changed" +msgstr "Contrasenya (de nou)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "Contrasenya" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "Password" +msgid "Set password" +msgstr "Contrasenya" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "Password" +msgid "Password reset" +msgstr "Contrasenya" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "Password" +msgid "Reset password" +msgstr "Contrasenya" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "perfil de registre" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Activation email sent" +msgstr "Re-enviar e-mails d'activació" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Resend Activation Email" +msgstr "Re-enviar e-mails d'activació" + +#~ msgid "Username" +#~ msgstr "Nom d'usuari" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "Aquest valor ha de contenir només lletres, números i guions baixos" + +#~ msgid "The two password fields didn't match." +#~ msgstr "Els dos camps de contrasenya no coincideixen" + +#~ msgid "Email address" +#~ msgstr "Adreça d'e-mail" diff --git a/pycons-site/registration/locale/cs/LC_MESSAGES/django.po b/pycons-site/registration/locale/cs/LC_MESSAGES/django.po new file mode 100644 index 0000000..bf2df51 --- /dev/null +++ b/pycons-site/registration/locale/cs/LC_MESSAGES/django.po @@ -0,0 +1,442 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , 2011. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Implayo s.r.o. \n" +"Language-Team: LANGUAGE \n" +"Language: cs\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Aktivovat uživatele" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Znovu odeslat aktivační e-maily" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Uživatel s tímto jménem již existuje." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Přečetl jsem si a souhlasím s podmínkami služby" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Musíte odsouhlasit podmínky služby pro pokračování v registraci." + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Tato e-mailová adresa se již používá. Prosím zadejte jinou e-mailovou adresu." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Používání volných e-mailových adres je zakázáno. Prosím zadejte jinou e-" +"mailovou adresu." + +#: models.py:274 +msgid "user" +msgstr "uživatel" + +#: models.py:276 +msgid "activation key" +msgstr "aktivační klíč" + +#: models.py:282 +msgid "registration profile" +msgstr "registrační profil" + +#: models.py:283 +msgid "registration profiles" +msgstr "registrační profily" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "aktivační klíč" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Account activation failed." +msgstr "Znovu odeslat aktivační e-maily" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "registrační profil" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "aktivační klíč" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "Password (again)" +msgid "Password changed" +msgstr "Heslo (znovu)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "Heslo" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "Password" +msgid "Set password" +msgstr "Heslo" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "Password" +msgid "Password reset" +msgstr "Heslo" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "Password" +msgid "Reset password" +msgstr "Heslo" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "registrační profil" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Activation email sent" +msgstr "Znovu odeslat aktivační e-maily" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Resend Activation Email" +msgstr "Znovu odeslat aktivační e-maily" + +#~ msgid "username" +#~ msgstr "uživatelské jméno" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "Tato hodnota může obsahovat pouze písmena, čísla a podtržítka." + +#~ msgid "Email address" +#~ msgstr "E-mailová adresa" + +#~ msgid "The two password fields didn't match." +#~ msgstr "Zadaná 2 hesla se neshodují." diff --git a/pycons-site/registration/locale/da/LC_MESSAGES/django.po b/pycons-site/registration/locale/da/LC_MESSAGES/django.po new file mode 100644 index 0000000..ce56b08 --- /dev/null +++ b/pycons-site/registration/locale/da/LC_MESSAGES/django.po @@ -0,0 +1,441 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Rune Bromer , 2007-2009. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.8 \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: \n" +"Last-Translator: Rune Bromer \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Aktiver brugere" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Gensend aktiveringsemails" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Der findes allerede en bruger med dette brugernavn." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "I har l¾st og accepterer betingelserne." + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Du skal acceptere betingelserne for at registere" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Denne emailadresse er allerede i brug. Benyt venligst en anden. " + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Registrering med gratis emailadresser er ikke muligt. V¾lg venligst en anden " +"emailadresse" + +#: models.py:274 +msgid "user" +msgstr "bruger" + +#: models.py:276 +msgid "activation key" +msgstr "Aktiveringsn¿gle" + +#: models.py:282 +msgid "registration profile" +msgstr "Registreringsprofil" + +#: models.py:283 +msgid "registration profiles" +msgstr "Registreringprofiler" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "Aktiveringsn¿gle" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Account activation failed." +msgstr "Gensend aktiveringsemails" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "Registreringsprofil" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "Aktiveringsn¿gle" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "Password (again)" +msgid "Password changed" +msgstr "Password (gentag)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "Password" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "Password" +msgid "Set password" +msgstr "Password" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "Password" +msgid "Password reset" +msgstr "Password" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "Password" +msgid "Reset password" +msgstr "Password" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "Registreringsprofil" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Activation email sent" +msgstr "Gensend aktiveringsemails" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Resend Activation Email" +msgstr "Gensend aktiveringsemails" + +#~ msgid "Username" +#~ msgstr "Brugernavn" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "V¾rdien mŒ kun indeholde bogstaver, tal og underscore." + +#~ msgid "Email address" +#~ msgstr "E-mailadresse" + +#~ msgid "The two password fields didn't match." +#~ msgstr "De 2 passwordfelter er ikke ens." diff --git a/pycons-site/registration/locale/de/LC_MESSAGES/django.po b/pycons-site/registration/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..c8e7bbd --- /dev/null +++ b/pycons-site/registration/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,529 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Jannis Leidel , 2007-2009. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.8 \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2007-09-29 16:50+0200\n" +"Last-Translator: Michael Schmid \n" +"Language-Team: Deutsch \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Benutzer aktivieren" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Aktivierungs-E-Mail erneut senden" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Dieser Benutzername ist bereits vergeben." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Ich habe die Nutzungsvereinbarung gelesen und stimme ihr zu" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Sie müssen der Nutzungsvereinbarung zustimmen, um sich zu registrieren" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Diese E-Mail-Adresse wird schon genutzt. Bitte geben Sie eine andere E-Mail-" +"Adresse an." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Die Registrierung mit einer kostenlosen E-Mail-Adresse ist untersagt. Bitte " +"geben Sie eine andere E-Mail-Adresse an." + +#: models.py:274 +msgid "user" +msgstr "Benutzer" + +#: models.py:276 +msgid "activation key" +msgstr "Aktivierungsschlüssel" + +#: models.py:282 +msgid "registration profile" +msgstr "Registrierungsprofil" + +#: models.py:283 +msgid "registration profiles" +msgstr "Registrierungsprofile" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "Aktivierungsschlüssel" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Account activation failed." +msgstr "Aktivierungs-E-Mail erneut senden" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "Account aktiviert" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "Ihr Account ist nun aktiviert." + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "Sie können sich jetzt einloggen." + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" +"Sobald ein Administrator Ihren Account aktiviert hat können Sie sich " +"einloggen." + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "Registrierungsprofil" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" +"\n" +" Sie (oder jemand der vorgibt Sie zu sein) möchte einen Account auf\n" +" %(site_name)s registrieren. Falls Sie es nicht waren, ignorieren Sie " +"diese E-Mail\n" +" und Ihre Adresse wird aus unserem System gelöscht.\n" +" " + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" +"\n" +" Um diesen Account zu aktivieren klicken Sie bitte auf den folgenden Link " +"innerhalb der nächsten next\n" +" %(expiration_days)s Tage:\n" +" " + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" +"\n" +" Mit freundlichen Grüßen,,\n" +" %(site_name)s Management\n" +" " + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" +"\n" +"Sie (oder jemand der vorgibt Sie zu sein) möchten einen Account auf\n" +"%(site_name)s registrieren. Falls es nicht Sie waren, ignorieren Sie bitte " +"diese E-Mail\n" +"und Ihre Adresse wird aus unserem System gelöscht.\n" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" +"\n" +"Um diesen Account zu aktivieren klicken Sie bitte auf den folgenden Link " +"innerhalb der nächsten\n" +"%(expiration_days)s Tage:\n" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" +"\n" +"Mit freundlichen Grüßen,\n" +"%(site_name)s Management\n" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "Aktivierungsschlüssel" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "Genehmigung fehlgeschlagen" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "Account Genehmigung fehlgeschlagen." + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "Account genehmigt" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "Der Account des Nutzers ist jetzt genehmigt." + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "Admin Genehmigung" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" +"\n" +" Ihr Account ist nun genehmigt. Sie können sich \n" +" " + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "einloggen" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" +"\n" +"Ihr Account ist nun genehmigt. Sie können sich über den folgenden Link " +"einloggen\n" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" +"\n" +" Der folgende Nutzer (%(user)s) möchte einen Account auf\n" +" %(site_name)s erstellen.\n" +" " + +#: templates/registration/admin_approve_email.html:17 +#, fuzzy +#| msgid "" +#| "\n" +#| " To approve this, please \n" +#| " " +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" +"\n" +" Um dies zu bestätigen, bitte \n" +" " + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "Hier klicken" + +#: templates/registration/admin_approve_email.txt:2 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| "The following user (%(user)s) has asked to register an account at\n" +#| "%(site_name)s. \n" +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" +"\n" +"Der folgende Nutzer (%(user)s) möchte einen Account auf\n" +"%(site_name)s erstellen. \n" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" +"\n" +"Zur Bestätigung bitte auf den folgenden Link klicken.\n" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "Account wartet auf Genehmigung" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +#, fuzzy +#| msgid "Log In" +msgid "Log in" +msgstr "Login" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "Passwort vergessen?" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "Zurücksetzen" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "Noch kein Mitglied?" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "Registrieren" + +#: templates/registration/logout.html:4 +#, fuzzy +#| msgid "Logged in" +msgid "Logged out" +msgstr "Eingeloggt" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "Erfolgreich ausgeloggt" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "Password (again)" +msgid "Password changed" +msgstr "Passwort (wiederholen)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "Passwort erfolgreich geändert!" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "Passwort ändern" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "Passwort-Zurücksetzung komplett" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "Ihr Passwort wurde zurückgesetzt!" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "Sie können sich jetzt einloggen" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "Bestätige Passwort-Zurücksetzung" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "Neues Passwort unten eingeben um Passwort zurückzusetzen" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "Password" +msgid "Set password" +msgstr "Passwort ändern" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "Password (again)" +msgid "Password reset" +msgstr "Passwort (wiederholen)" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" +"\n" +" Wir haben Ihnen eine E-Mail mit einem Link zum Zurücksetzen des " +"Passworts geschickt.Bitte prüfen Sie Ihre E-Mail und klicken Sie auf den " +"Link um Fortzusetzen.\n" +" " + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "Grüße" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" +"\n" +"Sie erhalten diese E-Mail, weil Sie (oder jemand der angibt Sie zu sein)\n" +"die Zurücksetzung des Passwort auf %(domain)s angefragt hat. Falls Sie\n" +"das Passwort nicht zurücksetzen wollen ignorieren Sie bitte diese " +"Nachricht.\n" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" +"\n" +"Um Ihr Passwort zurückzusetzen klicken Sie bitte auf den folgenden Link oder " +"kopieren Sie ihn und fügen ihn\n" +"in Ihrem Browser ein:\n" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "Ihr Nutzername, für den Fall, dass Sie ihn vergessen haben:" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "Beste Grüße" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "Management" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "Password" +msgid "Reset password" +msgstr "Passwort ändern" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" +"\n" +" Passwort vergessen? E-Mail im nachfolgenden Formular angeben und wir " +"senden Ihnen eine Anleitung um ein Neues zu erstellen.\n" +" " + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "Registrierungsprofil" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" +"Entschuldigung, die Registrierung ist derzeit geschlossen. Schauen Sie " +"später nochmal vorbei." + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Activation email sent" +msgstr "Aktivierungs-E-Mail erneut senden" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" +"Bitte überprüfen Sie Ihre E-Mail um den Registrierungs-Prozess abzuschließen." + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "Für einen Account registrieren" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "Absenden" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "Account Aktivierung erneut gesendet" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" +"\n" +" Wir haben Ihnen eine E-Mail an %(email)s mit weiteren Instruktionen " +"gesendet.\n" +" " + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Resend Activation Email" +msgstr "Aktivierungs-E-Mail erneut senden" + +#~ msgid "Username" +#~ msgstr "Benutzername" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "" +#~ "Dieser Wert darf nur Buchstaben, Ziffern und Unterstriche enthalten." + +#~ msgid "Email address" +#~ msgstr "E-Mail-Adresse" + +#~ msgid "The two password fields didn't match." +#~ msgstr "Die beiden Passwörter sind nicht identisch." diff --git a/pycons-site/registration/locale/el/LC_MESSAGES/django.po b/pycons-site/registration/locale/el/LC_MESSAGES/django.po new file mode 100644 index 0000000..f01a3fd --- /dev/null +++ b/pycons-site/registration/locale/el/LC_MESSAGES/django.po @@ -0,0 +1,468 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Panos Laganakos , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2007-11-14 21:50+0200\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Ενεργοποίηση χρηστών" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Να σταλεί ξανά το κλειδί ενεργοποίησης" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Υπάρχει ήδη χρήστης με αυτό το όνομα." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Διάβασα τους όρους χρήσης και συμφωνώ." + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Πρέπει να συμφωνήσετε με τους όρους για να εγγραφείτε" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Αυτή η διεύθυνση email χρησιμοποιείται ήδη. " +"Προσδιορίστε άλλη." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Η εγγραφή με δωρεάν διευθύνσεις email απαγορεύεται. " +"Προσδιορίστε άλλη διεύθυνση." + +#: models.py:274 +msgid "user" +msgstr "χρήστης" + +#: models.py:276 +msgid "activation key" +msgstr "κλειδί ενεργοποίησης" + +#: models.py:282 +msgid "registration profile" +msgstr "προφίλ εγγραφής" + +#: models.py:283 +msgid "registration profiles" +msgstr "προφίλ εγγραφών" + +#: templates/registration/activate.html:4 +msgid "Activation Failure" +msgstr "Αποτυχία ενεργοποίησης" + +#: templates/registration/activate.html:7 +msgid "Account activation failed." +msgstr "Η ενεργοποίηση του λογαριασμού απέτυχε." + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "Λογαριασμός ενεργοποιημένος" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "Ο λογαριασμός σας ενεργοποιήθηκε." + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "Μπορείτε να πραγματοποιήσετε είσοδο στο σύστημα." + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" +"Θα μπορέσετε να πραγματοποιήσετε είσοδο στο σύστημα μόλις ένας " +"διαχειριστής ενεργοποιήσει το λογαριασμό σας." + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +msgid "registration" +msgstr "εγγραφή" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" +"\n" +" Κάποιος ζήτησε να δημιουργηθεί λογαριασμός για σας στο %(site_name)s.\n" +" Αν δεν ήσασταν εσείς, αγνοήστε αυτό το email και η διεύθυνσή σας θα\n" +" διαγραφεί από τα αρχεία μας.\n" +" " + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" +"\n" +" Για να ενεργοποιηθεί ο λογαριασμός, ακολουθήστε τον παρακάτω σύνδεσμο\n" +" πριν περάσουν %(expiration_days)s μέρες:\n" +" " + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" +"\n" +" Με εκτίμηση,\n" +" Η διαχείριση του %(site_name)s\n" +" " + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" +"\n" +"Κάποιος ζήτησε να δημιουργηθεί λογαριασμός για σας στο %(site_name)s.\n" +"Αν δεν ήσασταν εσείς, αγνοήστε αυτό το email και η διεύθυνσή σας θα\n" +"διαγραφεί από τα αρχεία μας.\n" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" +"\n" +"Για να ενεργοποιηθεί ο λογαριασμός, ακολουθήστε τον παρακάτω σύνδεσμο\n" +"πριν περάσουν %(expiration_days)s μέρες:\n" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" +"\n" +"Με εκτίμηση,\n" +"Η διαχείριση του %(site_name)s\n" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +msgid "Account activation on" +msgstr "Ενεργοποίηση λογαριασμού στο" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "Αποτυχία έγκρισης" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "Η έγκριση του λογαριασμού απέτυχε." + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "Λογαριασμός εγκρίθηκε" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "Ο λογαριασμός του χρήστη εγκρίθηκε." + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "— έγκριση διαχειριστή" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" +"\n" +" Ο λογαριασμός σας έχει εγκριθεί. Μπορείτε να \n" +" " + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "πραγματοποιήσετε είσοδο." + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" +"\n" +"Ο λογαριασμός σας εγκρίθηκε. Μπορείτε τώρα να πραγματοποιήσετε είσοδο\n" +"με τον παρακάτω σύνδεσμο\n" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" +"\n" +" Ο παρακάτω χρήστης (%(user)s) ζήτησε να καταχωρήσει λογαριασμό στο\n" +" %(site_name)s.\n" +" " + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" +"\n" +" Για να εγκρίνετε, παρακαλώ\n" +" " + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "κλικ εδώ" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" +"\n" +"Ο παρακάτω χρήστης (%(user)s) ζήτησε να καταχωρήσει λογαριασμό στο\n" +"%(site_name)s.\n" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" +"\n" +"Για να εγκρίνετε, παρακαλώ ακολουθήστε τον παρακάτω σύνδεσμο.\n" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "Έγκριση λογαριασμού στο" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "Είσοδος" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "Ξεχάσατε το συνθηματικό;" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "Επαναφορά" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "Δεν έχετε λογαριασμό;" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "Εγγραφή" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "Αποσυνδεθήκατε" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "Αποσυνδεθήκατε επιτυχώς" + +#: templates/registration/password_change_done.html:4 +msgid "Password changed" +msgstr "Το συνθηματικό άλλαξε" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "Το συνθηματικό άλλαξε επιτυχώς" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +msgid "Change password" +msgstr "Αλλαγή συνθηματικού" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "Ολοκλήρωση επαναφοράς συνθηματικού" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "Το νέο συνθηματικό σας είναι έτοιμο." + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "Μπορείτε τώρα να πραγματοποιήσετε είσοδο" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "Επιβεβαίωση ορισμού νέου συνθηματικού" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "Προσδιορίστε νέο συνθηματικό:" + +#: templates/registration/password_reset_confirm.html:18 +msgid "Set password" +msgstr "Ορισμός συνθηματικού" + +#: templates/registration/password_reset_done.html:4 +msgid "Password reset" +msgstr "Επαναφορά συνθηματικού" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" +"\n" +" Σας στείλαμε email με σύνδεσμο για ορισμό νέου συνθηματικού.\n" +" Ελέγξτε το email σας και ακολουθήστε το σύνδεσμο.\n" +" " + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "Γεια σας" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" +"\n" +"Λαμβάνετε αυτό το email γιατί ζητήσατε να οριστεί νέο συνθηματικό\n" +"στον ιστότοπο %(domain)s. Αν δεν θέλετε να ορίσετε νέο συνθηματικό,\n" +"αγνοήστε αυτό το μήνυμα.\n" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" +"\n" +"Για να ορίσετε νέο συνθηματικό, ακολουθήστε τον παρακάτω σύνδεσμο\n" +"ή αντιγράψτε τον στον περιηγητή σας:\n" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "Το όνομα χρήστη σας, σε περίπτωση που το ξεχάσατε:" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "Χαιρετισμούς" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "— Η διαχείριση" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +msgid "Reset password" +msgstr "Επαναφορά συνθηματικού" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" +"\n" +" Ξεχάσατε το συνθηματικό; Προσδιορίστε το email σας και θα σας στείλουμε " +"οδηγίες για να ορίσετε νέο.\n" +" " + +#: templates/registration/registration_closed.html:4 +msgid "Registration is closed" +msgstr "Δεν γίνονται εγγραφές" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "Δεν επιτρέπονται εγγραφές επί του παρόντος. Δοκιμάστε αργότερα." + +#: templates/registration/registration_complete.html:4 +msgid "Activation email sent" +msgstr "Εστάλη το email για ενεργοποίηση" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "Ελέγξτε το email σας για να ολοκληρώσετε τη διαδικασία εγγραφής." + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "Εγγραφή" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "Υποβολή" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "Το email ενεργοποίησης εστάλη εκ νέου" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" +"\n" +" Στείλαμε email με οδηγίες στο %(email)s.\n" +" " + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "Εκ νέου αποστολή του email ενεργοποίησης" diff --git a/pycons-site/registration/locale/en/LC_MESSAGES/django.po b/pycons-site/registration/locale/en/LC_MESSAGES/django.po new file mode 100644 index 0000000..3f2e286 --- /dev/null +++ b/pycons-site/registration/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,403 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" + +#: models.py:274 +msgid "user" +msgstr "" + +#: models.py:276 +msgid "activation key" +msgstr "" + +#: models.py:282 +msgid "registration profile" +msgstr "" + +#: models.py:283 +msgid "registration profiles" +msgstr "" + +#: templates/registration/activate.html:4 +msgid "Activation Failure" +msgstr "" + +#: templates/registration/activate.html:7 +msgid "Account activation failed." +msgstr "" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +msgid "registration" +msgstr "" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +msgid "Account activation on" +msgstr "" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +msgid "Password changed" +msgstr "" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +msgid "Change password" +msgstr "" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +msgid "Set password" +msgstr "" + +#: templates/registration/password_reset_done.html:4 +msgid "Password reset" +msgstr "" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +msgid "Reset password" +msgstr "" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +msgid "Registration is closed" +msgstr "" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +msgid "Activation email sent" +msgstr "" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" diff --git a/pycons-site/registration/locale/es/LC_MESSAGES/django.po b/pycons-site/registration/locale/es/LC_MESSAGES/django.po new file mode 100644 index 0000000..4a3ba23 --- /dev/null +++ b/pycons-site/registration/locale/es/LC_MESSAGES/django.po @@ -0,0 +1,493 @@ +# Spanish translation for django-registration. +# Copyright (C) 2007, James Bennet +# This file is distributed under the same license as the registration package. +# Ernesto Rico Schmidt , 2008. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.3 \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-04-10 22:33-0400\n" +"PO-Revision-Date: 2018-04-11 08:50+0200\n" +"Last-Translator: Jose Antonio Martin \n" +"Language-Team: Español \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Activar usuarios" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Reenviar correos de activación" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "Correo Electrónico" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Ya existe un usuario con ese nombre de usuario." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "He leído y acepto los Términos de Servicio" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Debe aceptar los términos para registrarse" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"La dirección de correo electrónico ya está siendo usada. Por favor " +"proporcione otra dirección." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"El registro usando una dirección de correo electrónico gratuita está " +"prohibido. Por favor proporcione otra dirección." + +#: models.py:304 +msgid "user" +msgstr "usuario" + +#: models.py:306 +msgid "activation key" +msgstr "clave de activación" + +#: models.py:312 +msgid "registration profile" +msgstr "perfil de registro" + +#: models.py:313 +msgid "registration profiles" +msgstr "perfiles de registro" + +#: templates/registration/activate.html:4 +msgid "Activation Failure" +msgstr "Fallo de activación" + +#: templates/registration/activate.html:7 +msgid "Account activation failed." +msgstr "La activación de la cuenta ha fallado." + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "Cuenta Activada" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "Tu cuenta está activada." + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "Puedes acceder." + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "Podrás acceder después de que un administrador active tu cuenta." + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +msgid "registration" +msgstr "registro" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" +"\n" +" Usted (o alguien que se hace pasar por usted) ha solicitado dar de alta " +"una cuenta en\n" +" %(site_name)s. Si no ha sido usted, por favor, ignore este email\n" +" y su dirección será eliminada de nuestros registros.\n" +" " + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" +"\n" +" Para activar esta cuenta, por favor pulse el siguiente enlace durante " +"los próximos\n" +" %(expiration_days)s días:\n" +" " + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" +"\n" +" Atentamente,\n" +" los administradores de %(site_name)s\n" +" " + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" +"\n" +"Usted (o alguien que se hace pasar por usted) ha solicitado dar de alta una " +"cuenta en\n" +"%(site_name)s. Si no ha sido usted, por favor ignore este correo " +"electrónico\n" +"y su dirección será borrada de nuestros registros.\n" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" +"\n" +"Para activar esta cuenta, por favor pulse el siguiente enlace durante los " +"próximos\n" +"%(expiration_days)s días:\n" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" +"\n" +"Atentamente,\n" +"los administradores de %(site_name)s\n" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +msgid "Account activation on" +msgstr "Activación de cuenta en" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "Fallo de aprobación" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "La aprobación de la cuenta falló." + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "Cuenta Aprobada" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "La cuenta del usuario ha sido aprobada." + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "aprobación de administrador" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" +"\n" +" Su cuenta ha sido aprobada. Puede \n" +" " + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "iniciar sesión." + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" +"\n" +"Su cuenta ha sido aprobada. Puede acceder usando el siguiente enlace\n" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" +"\n" +" El siguiente usuario (%(user)s) ha solicitado dar de alta una cuenta en\n" +" %(site_name)s.\n" +" " + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" +"\n" +" Para aprobarla, por favor\n" +" " + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "pulse aquí" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" +"\n" +"El siguiente usuario (%(user)s) ha solicitado dar de alta una cuenta en\n" +"%(site_name)s.\n" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" +"\n" +"Para aprobarla, por favor pulse el siguiente enlace.\n" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "Aprobación de la cuenta en" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "Iniciar sesión" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "¿Olvidó su contraseña?" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "Restablézcala" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "¿Aún no es miembro?" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "Regístrese" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "Desconectado" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "Se desconectó con éxito" + +#: templates/registration/password_change_done.html:4 +msgid "Password changed" +msgstr "Contraseña cambiada" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "¡Se cambió la contraseña con éxito!" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +msgid "Change password" +msgstr "Cambiar contraseña" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "Reinicio de contraseña completado" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "¡Su contraseña ha sido restablecida!" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "Ahora puede iniciar sesión" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "Confirmar restablecimiento de contraseña" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "Introduzca su nueva contraseña para restablecerla:" + +#: templates/registration/password_reset_confirm.html:18 +msgid "Set password" +msgstr "Configurar contraseña" + +#: templates/registration/password_reset_done.html:4 +msgid "Password reset" +msgstr "Reinicio de contraseña" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" +"\n" +" Le hemos enviado un email con un enlace para restablecer su contraseña. " +"Por favor compruebe\n" +" su correo electrónico y pulse el enlace para continuar.\n" +" " + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "Saludos" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" +"\n" +"Está recibiendo este email porque usted (o alguien que se hace pasar por " +"usted)\n" +"solicitó que su contraseña fuera restablecida en %(domain)s. Si usted no " +"desea\n" +"restablecer su contraseña, por favor ignore este mensaje.\n" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" +"\n" +"Para restablecer su contraseña, por favor pulse el siguiente enlace, o " +"cópielo y péguelo en su navegador:\n" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "Su usuario, por si lo hubiera olvidado:" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "Saludos cordiales" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "Administración" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +msgid "Reset password" +msgstr "Restablecer contraseña" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" +"\n" +" ¿Olvidó su contraseña? Introduzca su email en el siguiente formulario y " +"le enviaremos instrucciones para restablecerla.\n" +" " + +#: templates/registration/registration_closed.html:4 +msgid "Registration is closed" +msgstr "El registro está cerrado" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" +"Lo sentimos, pero el registro está cerrado en este momento. Inténtelo más " +"tarde." + +#: templates/registration/registration_complete.html:4 +msgid "Activation email sent" +msgstr "Email de activación enviado" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" +"Por favor revise su correo electrónico para completar el proceso de registro." + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "Registre una cuenta" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "Enviar" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "Activación de Cuenta Reenviada" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" +"\n" +" Le hemos enviado un email a %(email)s con más instrucciones.\n" +" " + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "Reenviar Email de Activación" + +#~ msgid "username" +#~ msgstr "nombre de usuario" + +#~ msgid "email address" +#~ msgstr "dirección de coreo electrónico" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "" +#~ "Los nombres de usuarios sólo pueden contener letras, números y guiones " +#~ "bajos" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "Este nombre de usuario ya está ocupado. Por favor escoge otro" + +#~ msgid "You must type the same password each time" +#~ msgstr "Tienes que introducir la misma contraseña cada vez" diff --git a/pycons-site/registration/locale/es_AR/LC_MESSAGES/django.po b/pycons-site/registration/locale/es_AR/LC_MESSAGES/django.po new file mode 100644 index 0000000..c97ead5 --- /dev/null +++ b/pycons-site/registration/locale/es_AR/LC_MESSAGES/django.po @@ -0,0 +1,446 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2008 Leonardo Manuel Rocha +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "clave de activación" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "He leído y estoy de acuerdo con las Condiciones de Servicio" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Debe estar de acuerdo con las Condiciones para poder registrarse" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Esa dirección de e-mail ya está en uso. Por favor provea otra dirección." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"La registración con un e-mail gratuito está prohibida. Por favor de una " +"dirección de e-mail diferente." + +#: models.py:274 +msgid "user" +msgstr "usuario" + +#: models.py:276 +msgid "activation key" +msgstr "clave de activación" + +#: models.py:282 +msgid "registration profile" +msgstr "perfil de registro" + +#: models.py:283 +msgid "registration profiles" +msgstr "perfiles de registro" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "clave de activación" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "clave de activación" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "perfil de registro" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "clave de activación" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "contraseña (nuevamente)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "contraseña" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "contraseña" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "contraseña" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "contraseña" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "perfil de registro" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "clave de activación" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "nombre de usuario" + +#~ msgid "email address" +#~ msgstr "dirección de e-mail" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "" +#~ "El nombre de usuario solo puede contener letras, números y guiones bajos" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "Ese nombre de usuario ya está asignado. Por favor elija otro." + +#~ msgid "You must type the same password each time" +#~ msgstr "Debe tipear la misma contraseña cada vez" diff --git a/pycons-site/registration/locale/fa/LC_MESSAGES/django.po b/pycons-site/registration/locale/fa/LC_MESSAGES/django.po new file mode 100644 index 0000000..6380a00 --- /dev/null +++ b/pycons-site/registration/locale/fa/LC_MESSAGES/django.po @@ -0,0 +1,444 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Mohsen Mansouryar , 2011. +# +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.8\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2011-11-18 01:11+0330\n" +"Last-Translator: Mohsen Mansouryar \n" +"Language-Team: erixe \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Persian\n" +"X-Poedit-Country: IRAN, ISLAMIC REPUBLIC OF\n" +"X-Poedit-SourceCharset: utf-8\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "کد فعالسازی" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "من شرایط استفاده از این سرویس را مطالعه کرده و می پذیرم." + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "شما باید شرایط عضویت را بپذیرید!" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "این آدرس استفاده شده است. لطفا آدرس دیگری ارائه دهید." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "ثبت نام با استفاده از پست های الکترونیکی رایگان امکان پذیر نمی باشد." + +#: models.py:274 +msgid "user" +msgstr "کاربر" + +#: models.py:276 +msgid "activation key" +msgstr "کد فعالسازی" + +#: models.py:282 +msgid "registration profile" +msgstr "مشخصات ثبت نام" + +#: models.py:283 +msgid "registration profiles" +msgstr "پروفایل های ثبت نام" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "کد فعالسازی" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "کد فعالسازی" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "مشخصات ثبت نام" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "کد فعالسازی" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "گذرواژه(تکرار)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "گذرواژه" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "گذرواژه" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "گذرواژه" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "گذرواژه" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "مشخصات ثبت نام" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "کد فعالسازی" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "نام کاربری" + +#~ msgid "email address" +#~ msgstr "پست الکترونیکی" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "نام های کاربری تنها می توانند شامل حرف، رقم و یا _ باشند." + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "این نام کاربری گرفته شده است. لطفا نام دیگری انتخاب کنید." + +#~ msgid "You must type the same password each time" +#~ msgstr "دو گذرواژه باید مطابق باشند!" diff --git a/pycons-site/registration/locale/fr/LC_MESSAGES/django.po b/pycons-site/registration/locale/fr/LC_MESSAGES/django.po new file mode 100644 index 0000000..119d1a6 --- /dev/null +++ b/pycons-site/registration/locale/fr/LC_MESSAGES/django.po @@ -0,0 +1,530 @@ +# django-registration French translation. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the django-registration package. +# Samuel Adam , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.8 alpha-1 \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2010-07-01 14:30+0200\n" +"Last-Translator: Jean-Marc Porcherot \n" +"Language-Team: Français \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Activer les utilisateurs" + +#: admin.py:44 +#, fuzzy +#| msgid "Activation email sent" +msgid "Re-send activation emails" +msgstr "Le mail d'activation a été envoyé" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Ce nom d'utilisateur est déjà utilisé." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "J'ai lu et accepté les Conditions Générales d'Utilisation" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Vous devez accepter les conditions d'utilisation pour vous inscrire" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Cette adresse courriel est déjà utilisée. Veuillez en indiquer une autre." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"L'inscription avec adresse courriel de compte gratuit est interdite. " +"Veuillez en indiquer une autre." + +#: models.py:274 +msgid "user" +msgstr "utilisateur" + +#: models.py:276 +#, fuzzy +#| msgid "Account activation on" +msgid "activation key" +msgstr "Activation du compte sur" + +#: models.py:282 +#, fuzzy +#| msgid "registration profiles" +msgid "registration profile" +msgstr "profils d'inscription" + +#: models.py:283 +msgid "registration profiles" +msgstr "profils d'inscription" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "Activation email sent" +msgid "Activation Failure" +msgstr "Le mail d'activation a été envoyé" + +#: templates/registration/activate.html:7 +msgid "Account activation failed." +msgstr "Erreur lors de l'activation du compte" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "Compte activé" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "Votre compte est activé" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "Vous pouvez vous connecter." + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +msgid "registration" +msgstr "profil d'inscription" + +#: templates/registration/activation_email.html:11 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| " You (or someone pretending to be you) have asked to register an " +#| "account at\n" +#| " %(site.name)s. If this wasn't you, please ignore this email\n" +#| " and your address will be removed from our records.\n" +#| " " +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" +"\n" +" Vous (ou quelqu'un se faisant passer pour vous) a demandé à créer un " +"compte sur le site\n" +" %(site.name)s. Si ce n'étais pas vous, veuillez ignorer cet email\n" +" et votre adresse sera retirée de notre base.\n" +" " + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" +"\n" +" Pour activer votre compte, cliquez sur ce lien avant\n" +" %(expiration_days)s jours:\n" +" " + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| " Sincerely,\n" +#| " %(site.name)s Management\n" +#| " " +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" +"\n" +" Cordialement,\n" +" " + +#: templates/registration/activation_email.txt:2 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| "You (or someone pretending to be you) have asked to register an account " +#| "at\n" +#| "%(site.name)s. If this wasn't you, please ignore this email\n" +#| "and your address will be removed from our records.\n" +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" +"\n" +" Vous (ou quelqu'un se faisant passer pour vous) a demandé à créer un " +"compte sur le site\n" +" %(site.name)s. Si ce n'étais pas vous, veuillez ignorer cet email\n" +" et votre adresse sera retirée de notre base.\n" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" +"\n" +" Pour activer votre compte, cliquez sur ce lien avant\n" +" %(expiration_days)s jours:\n" + +#: templates/registration/activation_email.txt:14 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| "Sincerely,\n" +#| "%(site.name)s Management\n" +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" +"\n" +" Cordialement,\n" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +msgid "Account activation on" +msgstr "Activation du compte sur" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +#, fuzzy +#| msgid "Account activation failed." +msgid "Account Approval failed." +msgstr "Erreur lors de l'activation du compte" + +#: templates/registration/admin_approve_complete.html:4 +#, fuzzy +#| msgid "Account Activated" +msgid "Account Approved" +msgstr "Compte activé" + +#: templates/registration/admin_approve_complete.html:8 +#, fuzzy +#| msgid "Your account is now activated." +msgid "The user's account is now approved." +msgstr "Votre compte est activé" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +#, fuzzy +#| msgid "Your account is now activated." +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "Votre compte est activé" + +#: templates/registration/admin_approve_complete_email.html:14 +#, fuzzy +#| msgid "log in" +msgid "log in." +msgstr "vous connecter" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +#, fuzzy +#| msgid "" +#| "\n" +#| "To activate this account, please click the following link within the " +#| "next\n" +#| "%(expiration_days)s days:\n" +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" +"\n" +" Pour activer votre compte, cliquez sur ce lien avant\n" +" %(expiration_days)s jours:\n" + +#: templates/registration/admin_approve_email_subject.txt:1 +#, fuzzy +#| msgid "Account activation on" +msgid "Account approval on" +msgstr "Activation du compte sur" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "Se connecter" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "Mot de passe oublié ?" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "Le renouveler" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "Vous n'êtes pas membre ?" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "Vous enregistrer" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "Déconnecté" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "Déconnecté avec succès" + +#: templates/registration/password_change_done.html:4 +msgid "Password changed" +msgstr "Mot de passe changé" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "Mot de passe changé avec succès" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +msgid "Change password" +msgstr "Mot de passe" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "Changement du mot de passe effectué" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "Votre mot de passe a été changé" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "Confirmer le renouvellement du mot de passe" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "Saisissez votre nouveau mot de passe" + +#: templates/registration/password_reset_confirm.html:18 +msgid "Set password" +msgstr "Modifiez votre mot de passe" + +#: templates/registration/password_reset_done.html:4 +msgid "Password reset" +msgstr "Mot de passe modifié" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" +"\n" +" Un lien vous a été envoyé pour le changement de votre mot de passe. " +"Veuillez consulter\n" +" votre boîte mail et cliquer sur le lien pour continuer.\n" +" " + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "Cher" + +#: templates/registration/password_reset_email.html:5 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| "You are receiving this email because you (or someone pretending to be " +#| "you)\n" +#| "requested that your password be reset on the %(domain)s site. If you do " +#| "not \n" +#| "wish to reset your password, please ignore this message.\n" +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" +"\n" +"Vous recevez ce courriel parce que vous (ou quelqu'un se faisant passer pour " +"vous)\n" +"avez demandé à renouveler votre mot de passe sur le site %(domain)s. \n" +"Si vous ne souhaitez pas renouveler votre mot de passe, veuillez ignorer ce " +"message.\n" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" +"\n" +"Pour renouveler votre mot de passe, veuillez cliquer sur le lien ci-" +"dessous,\n" +"ou le copier/coller dans la barre d'adresse de votre navigateur :\n" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "Rappel de votre nom d'utilisateur, si vous l'avez oublié :" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "Cordialement" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +msgid "Reset password" +msgstr "Renouveler le mot de passe" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" +"\n" +" Mot de passe oublié ? Saisissez votre adresse mail ci-dessous afin que " +"nous vous envoyions les instructions pour que vous puissiez le renouveler.\n" +" " + +#: templates/registration/registration_closed.html:4 +msgid "Registration is closed" +msgstr "L'inscription est indisponible" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" +"Désolé, l'inscription est indisponible pour le moment. Veuillez réessayer " +"plus tard" + +#: templates/registration/registration_complete.html:4 +msgid "Activation email sent" +msgstr "Le mail d'activation a été envoyé" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" +"Veuillez consulter votre boîte mail pour terminer le processus " +"d'enregistrement" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "Créer un compte" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "Envoyer" + +#: templates/registration/resend_activation_complete.html:4 +#, fuzzy +#| msgid "Account activation on" +msgid "Account Activation Resent" +msgstr "Activation du compte sur" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Activation email sent" +msgid "Resend Activation Email" +msgstr "Le mail d'activation a été envoyé" + +#~ msgid "You may now" +#~ msgstr "Vous pouvez maintenant" + +#~ msgid "Username" +#~ msgstr "Nom d'utilisateur" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "" +#~ "Cette valeur ne doit contenir que des lettres, chiffres et tirets bas." + +#~ msgid "Email address" +#~ msgstr "Adresse courriel" + +#~ msgid "The two password fields didn't match." +#~ msgstr "Les deux mots de passe ne correspondent pas." diff --git a/pycons-site/registration/locale/he/LC_MESSAGES/django.po b/pycons-site/registration/locale/he/LC_MESSAGES/django.po new file mode 100644 index 0000000..fde0460 --- /dev/null +++ b/pycons-site/registration/locale/he/LC_MESSAGES/django.po @@ -0,0 +1,444 @@ +# translation of registration. +# Copyright (C) 2008 THE registration'S COPYRIGHT HOLDER +# This file is distributed under the same license as the registration package. +# <>, 2008. +# , fuzzy +# <>, 2008. +# +# +msgid "" +msgstr "" +"Project-Id-Version: registration\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2008-02-10 02:05+0200\n" +"Last-Translator: Meir Kriheli \n" +"Language-Team: Hebrew\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "מפתח הפעלה" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "קראתי והסכמתי לתנאי השימוש" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "עליך להסכים לתנאי השימוש" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "כתובת הדואר האלקטרוני תפוסה כבר. נא לספק כתובת דואר אחרת." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "הרישום בעזרת תיבת דואר אלקטרוני חינמית אסור. נא לספק כתובת אחרת." + +#: models.py:274 +msgid "user" +msgstr "משתמש" + +#: models.py:276 +msgid "activation key" +msgstr "מפתח הפעלה" + +#: models.py:282 +msgid "registration profile" +msgstr "פרופיל רישום" + +#: models.py:283 +msgid "registration profiles" +msgstr "פרופילי רישום" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "מפתח הפעלה" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "מפתח הפעלה" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "פרופיל רישום" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "מפתח הפעלה" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "סיסמה (שוב)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "סיסמה" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "סיסמה" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "סיסמה" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "סיסמה" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "פרופיל רישום" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "מפתח הפעלה" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "שם משתמש" + +#~ msgid "email address" +#~ msgstr "דואר אלקטרוני" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "שמות משתמש יכולים להכיל רק אותיות, ספרות וקווים תחתונים" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "שם המשתמש תפוס כבר. נא לבחור אחר." + +#~ msgid "You must type the same password each time" +#~ msgstr "יש להקליד את אותה הסיסמה פעמיים" diff --git a/pycons-site/registration/locale/hr/LC_MESSAGES/django.po b/pycons-site/registration/locale/hr/LC_MESSAGES/django.po new file mode 100644 index 0000000..aa49528 --- /dev/null +++ b/pycons-site/registration/locale/hr/LC_MESSAGES/django.po @@ -0,0 +1,443 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.8.1beta\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2010-12-01 15:49+0100\n" +"Last-Translator: Enis Afgan \n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Croatian\n" +"X-Poedit-Country: CROATIA\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Aktiviraj korisnike" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Ponovno pošlji aktivacijski email" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Ovo korisničko ime već postoji." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Pročitao sam i slažem se s uvijetima uporabe." + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Morate se složiti sa uvijetima uporabe prije registracije." + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Ova email adresa je već korištena. Molimo da koristite drugu email adresu." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Registracija gdje se koristi besplati email servis nije dopuštena. Molimo da " +"koristite drugu email adresu." + +#: models.py:274 +msgid "user" +msgstr "Korisnik" + +#: models.py:276 +msgid "activation key" +msgstr "Aktivacijski ključ" + +#: models.py:282 +msgid "registration profile" +msgstr "Registracijski profil" + +#: models.py:283 +msgid "registration profiles" +msgstr "Registracijski profili" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "Aktivacijski ključ" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Account activation failed." +msgstr "Ponovno pošlji aktivacijski email" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "Registracijski profil" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "Aktivacijski ključ" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "Password (again)" +msgid "Password changed" +msgstr "Lozinka (ponovno)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "Lozinka" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "Password" +msgid "Set password" +msgstr "Lozinka" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "Password" +msgid "Password reset" +msgstr "Lozinka" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "Password" +msgid "Reset password" +msgstr "Lozinka" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "Registracijski profil" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Activation email sent" +msgstr "Ponovno pošlji aktivacijski email" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Resend Activation Email" +msgstr "Ponovno pošlji aktivacijski email" + +#~ msgid "username" +#~ msgstr "Korisničko ime" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "Ova vrijednost mora sadržavati samo slova, brojeve i podvlake." + +#~ msgid "Email address" +#~ msgstr "Email adresa" + +#~ msgid "The two password fields didn't match." +#~ msgstr "Oba polja za lozinku nisu ista." diff --git a/pycons-site/registration/locale/hu/LC_MESSAGES/django.po b/pycons-site/registration/locale/hu/LC_MESSAGES/django.po new file mode 100644 index 0000000..4c4b283 --- /dev/null +++ b/pycons-site/registration/locale/hu/LC_MESSAGES/django.po @@ -0,0 +1,478 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Gergely Bódi , 2015. +# +msgid "" +msgstr "" +"Project-Id-Version: django-register-redux 1.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"Language-Team: Hungarian \n" +"Language: \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" + +#: admin.py:26 +msgid "Activate users" +msgstr "Felhasználók aktiválása" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Aktiváló emailek újraküldése" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "E-mail" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Elolvastam és elfogadom a felhasználási feltételeket" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "El kell fogadnod a feltételeket a regisztráláshoz" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Ez az email cím már foglalt. Kérlek adj meg egy másikat." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "Ingyenes email címről regisztrálni tilos. Kérlek adj meg egy másikat." + +#: models.py:274 +msgid "user" +msgstr "felhasználó" + +#: models.py:276 +msgid "activation key" +msgstr "aktiválási kulcs" + +#: models.py:282 +msgid "registration profile" +msgstr "regisztrációs profil" + +#: models.py:283 +msgid "registration profiles" +msgstr "regisztrációs profilok" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "Activation email sent" +msgid "Activation Failure" +msgstr "Aktiváló email elküldve" + +#: templates/registration/activate.html:7 +msgid "Account activation failed." +msgstr "Fiók aktiválása sikertelen." + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "Fiók aktiválva" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "A fiókod aktiválásra került." + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "Mostantól bejelentkezhetsz." + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +msgid "registration" +msgstr "regisztráció" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" +"\n" +" Te (vagy valaki a nevedben) kérte egy fiók regisztrálását erre az " +"oldalra\n" +" %(site_name)s. Ha nem te voltál, hagyd ezt figyelmen kívül,\n" +" és a címed törölve lesz az adatbázisból.\n" +" " + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" +"\n" +" Az aktiváláshoz kattints az alábbi linkre\n" +" %(expiration_days)s napon belül:\n" +" " + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" +"\n" +" Üdvözlettel,\n" +" %(site_name)s Ügyfélszolgálat\n" +" " + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" +"\n" +"Te (vagy valaki a nevedben) kérte egy fiók regisztrálását erre az oldalra:\n" +"%(site_name)s. Ha nem te voltál, hagyd ezt figyelmen kívül, \n" +"és a címed törölve lesz az adatbázisból.\n" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" +"\n" +"Az aktiváláshoz kattints az alábbi linkre\n" +"%(expiration_days)s napon belül:\n" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" +"\n" +"Üdvözlettel,\n" +"%(site_name)s Ügyfélszolgálat\n" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +msgid "Account activation on" +msgstr "Fiók aktiválása a következőre:" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +#, fuzzy +#| msgid "Account activation failed." +msgid "Account Approval failed." +msgstr "Fiók aktiválása sikertelen." + +#: templates/registration/admin_approve_complete.html:4 +#, fuzzy +#| msgid "Account Activated" +msgid "Account Approved" +msgstr "Fiók aktiválva" + +#: templates/registration/admin_approve_complete.html:8 +#, fuzzy +#| msgid "Your account is now activated." +msgid "The user's account is now approved." +msgstr "A fiókod aktiválásra került." + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +#, fuzzy +#| msgid "Your account is now activated." +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "A fiókod aktiválásra került." + +#: templates/registration/admin_approve_complete_email.html:14 +#, fuzzy +#| msgid "log in" +msgid "log in." +msgstr "bejelentkezhetsz" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +#, fuzzy +#| msgid "" +#| "\n" +#| "To activate this account, please click the following link within the " +#| "next\n" +#| "%(expiration_days)s days:\n" +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" +"\n" +"Az aktiváláshoz kattints az alábbi linkre\n" +"%(expiration_days)s napon belül:\n" + +#: templates/registration/admin_approve_email_subject.txt:1 +#, fuzzy +#| msgid "Account activation on" +msgid "Account approval on" +msgstr "Fiók aktiválása a következőre:" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "Bejelentkezés" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "Elfelejtetted a jelszavad?" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "Visszaállítás" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "Még nem vagy tag?" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "Regisztráció" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "Kijelentkezés" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "Sikeresen kijelentkeztél" + +#: templates/registration/password_change_done.html:4 +msgid "Password changed" +msgstr "Megváltozott jelszó" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "A jelszavad sikeresen megváltozott!" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +msgid "Change password" +msgstr "Jelszó megváltoztatása" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "Jelszó megáltoztatva" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "A jelszavad sikeresen megváltoztatásra került!" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "Jelszó visszaállítás megerősítése" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "Írd ide az új jelszavad:" + +#: templates/registration/password_reset_confirm.html:18 +msgid "Set password" +msgstr "Jelszó beállítása" + +#: templates/registration/password_reset_done.html:4 +msgid "Password reset" +msgstr "Jelszó megváltoztatva" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" +"\n" +" Hamarosan kapsz egy levelet a jelszavad megváltozásáról. Nézd meg\n" +" a leveleid közt, és kattints a linkre a folytatáshoz.\n" +" " + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "Kedves" + +#: templates/registration/password_reset_email.html:5 +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| "You are receiving this email because you (or someone pretending to be " +#| "you)\n" +#| "requested that your password be reset on the %(domain)s site. If you do " +#| "not \n" +#| "wish to reset your password, please ignore this message.\n" +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" +"\n" +"Azért kaptad ezt a levelet, mert te (vagy valaki a nevedben)\n" +"jelszó visszaállítást kért a %(domain)s oldalon. Ha nem te akartad\n" +"visszaállítani a jelszavad, hagyd ezt figyelmen kívül, kérlek.\n" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" +"\n" +"A jelszavad visszaállításához kérlek kattints az alábbi linkre, vagy másold " +"ki\n" +"a böngésződbe:\n" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "A felhasználóneved, arra az esetre, ha elfelejtetted volna:" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "Minden jót" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "Ügyfélszolgálat" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +msgid "Reset password" +msgstr "Jelszó visszaállítás" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" +"\n" +" Elfelejtetted a jelszavad? Írd ide az email címed, és küldünk egy " +"levelet további instrukciókkal a megújításához.\n" +" " + +#: templates/registration/registration_closed.html:4 +msgid "Registration is closed" +msgstr "Regisztráció letiltva" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "Sajnálom, a regisztráció jelenleg le van tiltva. Gyere vissza később" + +#: templates/registration/registration_complete.html:4 +msgid "Activation email sent" +msgstr "Aktiváló email elküldve" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "Kérlek nézd meg a leveleid a regisztráció befejezéséhez." + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "Fiók regisztrálása" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "Küldés" + +#: templates/registration/resend_activation_complete.html:4 +#, fuzzy +#| msgid "Account activation on" +msgid "Account Activation Resent" +msgstr "Fiók aktiválása a következőre:" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Resend Activation Email" +msgstr "Aktiváló emailek újraküldése" + +#~ msgid "You may now" +#~ msgstr "Most már" diff --git a/pycons-site/registration/locale/is/LC_MESSAGES/django.po b/pycons-site/registration/locale/is/LC_MESSAGES/django.po new file mode 100644 index 0000000..2fddd67 --- /dev/null +++ b/pycons-site/registration/locale/is/LC_MESSAGES/django.po @@ -0,0 +1,441 @@ +# Icelandic translation of django-registration +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the django-registration +# package. +# Björn Kristinsson , 2009. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2009-01-22 12:49+0100\n" +"Last-Translator: Björn Kristinsson \n" +"Language-Team: Icelandic\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "einkennislykill" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Ég hef lesið og samþykki skilmálana" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Þetta netfang er þegar á skrá. Vinsamlegast notaðu annað netfang." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Óheimilt er að nota ókeypis netföng. Vinsamlegast notaðu annað netfang." + +#: models.py:274 +msgid "user" +msgstr "notandi" + +#: models.py:276 +msgid "activation key" +msgstr "einkennislykill" + +#: models.py:282 +msgid "registration profile" +msgstr "skráningarprófíll" + +#: models.py:283 +msgid "registration profiles" +msgstr "skráningarprófílar" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "einkennislykill" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "einkennislykill" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "skráningarprófíll" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "einkennislykill" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "lykilorð (aftur)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "lykilorð" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "lykilorð" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "lykilorð" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "lykilorð" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "skráningarprófíll" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "einkennislykill" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "notandanafn" + +#~ msgid "email address" +#~ msgstr "netfang" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "Þetta notendanafn er þegar á skrá. Vinsamlega reyndu annað." + +#~ msgid "You must type the same password each time" +#~ msgstr "Lykilorðin verða að vera eins " diff --git a/pycons-site/registration/locale/it/LC_MESSAGES/django.po b/pycons-site/registration/locale/it/LC_MESSAGES/django.po new file mode 100644 index 0000000..8fd4662 --- /dev/null +++ b/pycons-site/registration/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,445 @@ +# translation of django.po to Italian +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Nicola Larosa , 2008. +# Flavio Curella , 2011 +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.8 alpha-1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2011-08-04 12:41-0600\n" +"Last-Translator: Flavio Curella \n" +"Language-Team: Italiano \n" +"Language: \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" +"X-Poedit-Language: Italian\n" +"X-Poedit-Country: ITALY\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Attiva utenti" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Re-invia email di attivazione" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Questo nome utente è già usato." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Dichiaro di aver letto e di approvare le Condizioni di Servizio" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Per registrarsi bisogna approvare le condizioni" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Questo indirizzo email è già in uso. Inserisci un altro indirizzo email." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"La registrazione con indirizzi email gratis non è permessa. Inserisci un " +"altro indirizzo email." + +#: models.py:274 +msgid "user" +msgstr "utente" + +#: models.py:276 +msgid "activation key" +msgstr "chiave di attivazione" + +#: models.py:282 +msgid "registration profile" +msgstr "profilo di registrazione" + +#: models.py:283 +msgid "registration profiles" +msgstr "profili di registrazione" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "chiave di attivazione" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Account activation failed." +msgstr "Re-invia email di attivazione" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "profilo di registrazione" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "chiave di attivazione" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "Password (again)" +msgid "Password changed" +msgstr "Password (di nuovo)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "Password" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "Password" +msgid "Set password" +msgstr "Password" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "Password" +msgid "Password reset" +msgstr "Password" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "Password" +msgid "Reset password" +msgstr "Password" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "profilo di registrazione" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Activation email sent" +msgstr "Re-invia email di attivazione" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Resend Activation Email" +msgstr "Re-invia email di attivazione" + +#~ msgid "Username" +#~ msgstr "Nome utente" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "Questo valore può contenere solo lettere, numeri e sottolineature." + +#~ msgid "Email address" +#~ msgstr "indirizzo email" + +#~ msgid "The two password fields didn't match." +#~ msgstr "Le password inserite non coincidono." diff --git a/pycons-site/registration/locale/ja/LC_MESSAGES/django.po b/pycons-site/registration/locale/ja/LC_MESSAGES/django.po new file mode 100644 index 0000000..6121959 --- /dev/null +++ b/pycons-site/registration/locale/ja/LC_MESSAGES/django.po @@ -0,0 +1,447 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Shinya Okano , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.4 \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2008-01-31 10:20+0900\n" +"Last-Translator: Shinya Okano \n" +"Language-Team: Japanese \n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "アクティベーションキー" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "サービス利用規約を読み、同意します。" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "登録するためには規約に同意する必要があります。" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"このメールアドレスは既に使用されています。他のメールアドレスを指定して下さ" +"い。" + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"自由なメールアドレスを使用した登録は禁止されています。他のメールアドレスを指" +"定してください。" + +#: models.py:274 +msgid "user" +msgstr "ユーザ" + +#: models.py:276 +msgid "activation key" +msgstr "アクティベーションキー" + +#: models.py:282 +msgid "registration profile" +msgstr "登録プロファイル" + +#: models.py:283 +msgid "registration profiles" +msgstr "登録プロファイル" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "アクティベーションキー" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "アクティベーションキー" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "登録プロファイル" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "アクティベーションキー" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "パスワード (確認)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "パスワード" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "パスワード" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "パスワード" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "パスワード" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "登録プロファイル" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "アクティベーションキー" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "ユーザ名" + +#~ msgid "email address" +#~ msgstr "メールアドレス" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "ユーザ名には半角英数とアンダースコアのみが使用できます。" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "" +#~ "このユーザ名は既に使用されています。他のユーザ名を指定してください。" + +#~ msgid "You must type the same password each time" +#~ msgstr "同じパスワードを入力する必要があります。" diff --git a/pycons-site/registration/locale/ko/LC_MESSAGES/django.po b/pycons-site/registration/locale/ko/LC_MESSAGES/django.po new file mode 100644 index 0000000..2c0e5e1 --- /dev/null +++ b/pycons-site/registration/locale/ko/LC_MESSAGES/django.po @@ -0,0 +1,475 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Sam Kim , 2021. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-01-18 15:40+0900\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Sam Kim \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: .\admin.py:26 +msgid "Activate users" +msgstr "사용자 활성화" + +#: .\admin.py:44 +msgid "Re-send activation emails" +msgstr "활성화 이메일 재전송" + +#: .\forms.py:34 .\forms.py:112 +msgid "E-mail" +msgstr "이메일" + +#: .\forms.py:50 +msgid "A user with that username already exists." +msgstr "이미 같은 아이디로 사용자가 등록되어 있습니다." + +#: .\forms.py:62 +msgid "I have read and agree to the Terms of Service" +msgstr "약관을 읽었고 그 내용에 동의합니다." + +#: .\forms.py:63 +msgid "You must agree to the terms to register" +msgstr "약관에 동의 하셔야만 합니다." + +#: .\forms.py:79 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "이메일이 이미 사용중입니다. 다른 이메일을 등록해 주세요." + +#: .\forms.py:106 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "무료 이메일 계정으로 등록하실 수 없습니다. 다른 이메일을 등록해 주세요" + +#: .\models.py:308 +msgid "user" +msgstr "사용자" + +#: .\models.py:310 +msgid "activation key" +msgstr "활성화 키" + +#: .\models.py:316 +msgid "registration profile" +msgstr "등록 프로파일들" + +#: .\models.py:317 +msgid "registration profiles" +msgstr "등록 프로파일" + +#: .\templates\registration\activate.html:4 +msgid "Activation Failure" +msgstr "활성화 실패" + +#: .\templates\registration\activate.html:7 +msgid "Account activation failed." +msgstr "계정 활성화 실패" + +#: .\templates\registration\activation_complete.html:4 +#: .\templates\registration\activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "계정 활성화됨" + +#: .\templates\registration\activation_complete.html:8 +#: .\templates\registration\activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "당신의 계정이 활성화 되었습니다." + +#: .\templates\registration\activation_complete.html:10 +msgid "You can log in." +msgstr "로그인 할 수 있습니다." + +#: .\templates\registration\activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "사이트 관리자가 당신의 계정을 활성화해야 로그인할 수 있습니다." + +#: .\templates\registration\activation_email.html:6 +#: .\templates\registration\admin_approve_email.html:6 +msgid "registration" +msgstr "등록" + +#: .\templates\registration\activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" +"\n" +" %(site_name)s에 당신(또는 당신을 가장한 사람)으로부터 계정 등록이 요청되" +"었습니다\n" +" 당신이 아니라면 이메일을 무시해주세요.\n" +" 당신의 계정은 데이터베이스로부터 삭제될 예정입니다.\n" +" " + +#: .\templates\registration\activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" +"\n" +" 계정을 활성화하기 위해서, %(expiration_days)s일 내로 다음의 링크를 클릭" +"해주세요.\n" +" " + +#: .\templates\registration\activation_email.html:30 +#: .\templates\registration\admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" +"\n" +" 감사합니다,\n" +" %(site_name)s의 관리자\n" +" " + +#: .\templates\registration\activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" +"\n" +"%(site_name)s에 당신(또는 당신을 가장한 사람)으로부터 계정 등록이 요청되었습" +"니다. 당신이 아니라면 이메일을 무시해주세요.\n" +"당신의 계정은 데이터베이스로부터 삭제될 예정입니다.\n" + +#: .\templates\registration\activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" +"\n" +"계정을 활성화하기 위해서, %(expiration_days)s일 내로 다음의 링크를 클릭해주세" +"요.\n" + +#: .\templates\registration\activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" +"\n" +"감사합니다,\n" +"%(site_name)s의 관리자\n" + +#: .\templates\registration\activation_email_subject.txt:1 +#: .\templates\registration\admin_approve_complete_email_subject.txt:1 +msgid "Account activation on" +msgstr "계정 활성화" + +#: .\templates\registration\admin_approve.html:4 +msgid "Approval Failure" +msgstr "승인 실패" + +#: .\templates\registration\admin_approve.html:7 +msgid "Account Approval failed." +msgstr "계정 승인 실패" + +#: .\templates\registration\admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "계정 승인" + +#: .\templates\registration\admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "사용자의 계정이 승인되었습니다." + +#: .\templates\registration\admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "관리자 승인" + +#: .\templates\registration\admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" +"\n" +" 당신의 계정이 승인되었습니다. 당신은 이제 \n" +" " + +#: .\templates\registration\admin_approve_complete_email.html:14 +msgid "log in." +msgstr "로그인 할 수 있습니다." + +#: .\templates\registration\admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" +"\n" +"당신의 계정이 승인되었습니다. 당신은 다음의 링크를 통해 로그인할 수 있습니" +"다.\n" + +#: .\templates\registration\admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" +"\n" +" %(site_name)s에서 다음 사용자 (%(user)s)가 계정 등록을 요청했습니다.\n" +" .\n" +" " + +#: .\templates\registration\admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" +"\n" +" 승인을 위해서, \n" +" " + +#: .\templates\registration\admin_approve_email.html:20 +msgid "click here" +msgstr "여기를 클릭 해주세요." + +#: .\templates\registration\admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" +"\n" +"%(site_name)s에서 다음 사용자 (%(user)s)가 계정 등록을 요청했습니다.\n" + +#: .\templates\registration\admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" +"\n" +"승인을 위해서, 다음의 링크를 클릭 해주세요.\n" + +#: .\templates\registration\admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "계정이 승인 되었습니다." + +#: .\templates\registration\login.html:4 .\templates\registration\login.html:10 +msgid "Log in" +msgstr "로그인" + +#: .\templates\registration\login.html:14 +msgid "Forgot your password?" +msgstr "비밀번호를 잊으셨나요?" + +#: .\templates\registration\login.html:14 +msgid "Reset it" +msgstr "초기화" + +#: .\templates\registration\login.html:15 +msgid "Not a member?" +msgstr "회원이 아닌가요?" + +#: .\templates\registration\login.html:15 +msgid "Register" +msgstr "회원가입" + +#: .\templates\registration\logout.html:4 +msgid "Logged out" +msgstr "로그아웃 됨" + +#: .\templates\registration\logout.html:7 +msgid "Successfully logged out" +msgstr "로그아웃이 되었습니다." + +#: .\templates\registration\password_change_done.html:4 +msgid "Password changed" +msgstr "비밀번호가 변경되었습니다." + +#: .\templates\registration\password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "비밀번호가 성공적으로 변경되었습니다!" + +#: .\templates\registration\password_change_form.html:4 +#: .\templates\registration\password_change_form.html:10 +msgid "Change password" +msgstr "비밀번호 변경" + +#: .\templates\registration\password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "비밀번호 초기화가 완료되었습니다" + +#: .\templates\registration\password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "당신의 비밀번호가 초기화 되었습니다!" + +#: .\templates\registration\password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "이제 로그인 할 수 있습니다." + +#: .\templates\registration\password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "비밀번호 초기화 확인" + +#: .\templates\registration\password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "새 비밀번호를 입력하여 비밀번호를 재설정하세요." + +#: .\templates\registration\password_reset_confirm.html:18 +msgid "Set password" +msgstr "비밀번호 설정" + +#: .\templates\registration\password_reset_done.html:4 +msgid "Password reset" +msgstr "비밀번호 초기화" + +#: .\templates\registration\password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" +"\n" +" 비밀번호 재설정 링크가 포함 된 이메일을 보냈습니다. 당신의 이메일을 확인" +"해주세요.\n" +" 그리고 재설정을 위해 링크를 클릭해주세요.\n" +" " + +#: .\templates\registration\password_reset_email.html:3 +msgid "Greetings" +msgstr "반갑습니다" + +#: .\templates\registration\password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" +"\n" +"본 이메일은 당신(또는 당신을 가장 한 사람)때문에 발송되었습니다.\n" +"%(domain)s 사이트에서 암호를 재설정하도록 요청했습니다.\n" +"당신의 비밀번호를 초기화하길 원하지 않으면, 이 메시지를 무시하십시오.\n" + +#: .\templates\registration\password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" +"\n" +"당신의 비밀번호를 초기화하기 위해서, 다음 링크를 클릭하거나 복사하여 당신의 " +"브라우저에 붙여넣어주세요.\n" + +#: .\templates\registration\password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "사용자 아이디(잊어버린 경우):" + +#: .\templates\registration\password_reset_email.html:23 +msgid "Best regards" +msgstr "감사합니다" + +#: .\templates\registration\password_reset_email.html:24 +msgid "Management" +msgstr "관리자" + +#: .\templates\registration\password_reset_form.html:4 +#: .\templates\registration\password_reset_form.html:15 +msgid "Reset password" +msgstr "비밀번호 초기화" + +#: .\templates\registration\password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" +"\n" +" 비밀번호를 잃어버리셨나요? 아래 양식에 이메일을 입력해주세요.새로운 비밀" +"번호를 설정하는 절차를 이메일로 보내드리겠습니다.\n" +" " + +#: .\templates\registration\registration_closed.html:4 +msgid "Registration is closed" +msgstr "등록이 마감되었습니다" + +#: .\templates\registration\registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "죄송합니다. 등록이 마감되었습니다. 나중에 다시오세요." + +#: .\templates\registration\registration_complete.html:4 +msgid "Activation email sent" +msgstr "활성화 이메일이 전송되었습니다" + +#: .\templates\registration\registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "등록을 완료하기 위해 당신의 메일을 확인해주세요." + +#: .\templates\registration\registration_form.html:4 +msgid "Register for an account" +msgstr "계정 등록" + +#: .\templates\registration\registration_form.html:10 +#: .\templates\registration\resend_activation_form.html:10 +msgid "Submit" +msgstr "제출" + +#: .\templates\registration\resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "계정 활성화 재전송" + +#: .\templates\registration\resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" +"\n" +" 추가 절차가 포함 된 이메일을 %(email)s 로 보냈습니다.\n" +" " + +#: .\templates\registration\resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "활성화 이메일 재전송" + +#~ msgid "username" +#~ msgstr "사용자 아이디" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "이 곳에는 숫자, _, 영문 글자만 가능합니다." + +#~ msgid "Email address" +#~ msgstr "이메일 주소" + +#~ msgid "The two password fields didn't match." +#~ msgstr "비밀번호가 서로 일치하지 않습니다." diff --git a/pycons-site/registration/locale/nb/LC_MESSAGES/django.po b/pycons-site/registration/locale/nb/LC_MESSAGES/django.po new file mode 100644 index 0000000..048314f --- /dev/null +++ b/pycons-site/registration/locale/nb/LC_MESSAGES/django.po @@ -0,0 +1,443 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# jonklo , 2010. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.8\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2010-12-15 21:37+0100\n" +"Last-Translator: jonklo \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Aktiver brukere" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Send ny aktiveringsmail" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Det eksisterer allerede en bruker med dette brukernavnet." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Jeg har lest og godtar betingelsene" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Du må godta betingelsene for å registrere deg" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Denne e-postadressen er allerede i bruk. Vennligst oppgi en annen e-" +"postadresse." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Registrering med gratis e-postadresse er ikke tillatt. Vennligst oppgi en " +"annen e-postadresse." + +#: models.py:274 +msgid "user" +msgstr "bruker" + +#: models.py:276 +msgid "activation key" +msgstr "aktiveringsnøkkel" + +#: models.py:282 +msgid "registration profile" +msgstr "registrasjonsprofil" + +#: models.py:283 +msgid "registration profiles" +msgstr "registrasjonsprofiler" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "aktiveringsnøkkel" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Account activation failed." +msgstr "Send ny aktiveringsmail" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "registrasjonsprofil" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "aktiveringsnøkkel" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "Password (again)" +msgid "Password changed" +msgstr "Passord (gjenta)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "Passord" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "Password" +msgid "Set password" +msgstr "Passord" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "Password" +msgid "Password reset" +msgstr "Passord" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "Password" +msgid "Reset password" +msgstr "Passord" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "registrasjonsprofil" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Activation email sent" +msgstr "Send ny aktiveringsmail" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Resend Activation Email" +msgstr "Send ny aktiveringsmail" + +#~ msgid "Username" +#~ msgstr "Brukernavn" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "Dette feltet kan bare inneholde bokstaver, nummer og understreker." + +#~ msgid "Email address" +#~ msgstr "E-postadresse" + +#~ msgid "The two password fields didn't match." +#~ msgstr "De to passordfeltene er ikke like." diff --git a/pycons-site/registration/locale/nl/LC_MESSAGES/django.po b/pycons-site/registration/locale/nl/LC_MESSAGES/django.po new file mode 100644 index 0000000..a860391 --- /dev/null +++ b/pycons-site/registration/locale/nl/LC_MESSAGES/django.po @@ -0,0 +1,450 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: registration\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2008-08-14 13:25+0200\n" +"Last-Translator: Joost Cassee \n" +"Language-Team: LANGUAGE \n" +"Language: \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" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "activatiecode" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Ik heb de servicevoorwaarden gelezen en ga akkoord." + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "U moet akkoord gaan met de servicevoorwaarden om u te registreren." + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Dit e-mail adres is reeds in gebruik. Kiest u alstublieft een ander e-mail " +"adres." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"U kunt u niet registreren met een gratis e-mail adres. Kiest u alstublieft " +"een ander e-mail adres." + +#: models.py:274 +msgid "user" +msgstr "gebruiker" + +#: models.py:276 +msgid "activation key" +msgstr "activatiecode" + +#: models.py:282 +msgid "registration profile" +msgstr "registratieprofiel" + +#: models.py:283 +msgid "registration profiles" +msgstr "registratieprofielen" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "activatiecode" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "activatiecode" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "registratieprofiel" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "activatiecode" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "wachtwoord (opnieuw)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "wachtwoord" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "wachtwoord" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "wachtwoord" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "wachtwoord" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "registratieprofiel" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "activatiecode" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "gebruikersnaam" + +#~ msgid "email address" +#~ msgstr "e-mail adres" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "" +#~ "Gebruikersnamen kunnen alleen letters, nummer en liggende streepjes " +#~ "bevatten." + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "" +#~ "Deze gebruikersnaam is reeds in gebruik. Kiest u alstublieft een andere " +#~ "gebruikersnaam." + +#~ msgid "You must type the same password each time" +#~ msgstr "U moet twee maal hetzelfde wachtwoord typen." diff --git a/pycons-site/registration/locale/pl/LC_MESSAGES/django.po b/pycons-site/registration/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 0000000..9598e9c --- /dev/null +++ b/pycons-site/registration/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,445 @@ +# Polish translation for django-registration. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the django-registration package. +# Jarek Zgoda , 2007. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: 0.4\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2007-12-15 12:45+0100\n" +"Last-Translator: Jarek Zgoda \n" +"Language-Team: Polish \n" +"Language: pl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "klucz aktywacyjny" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Przeczytałem regulamin i akceptuję go" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Musisz zaakceptować regulamin, aby się zarejestrować" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Ten adres email jest już używany. Użyj innego adresu email." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Nie ma możliwości rejestracji przy użyciu darmowego adresu email. Użyj " +"innego adresu email." + +#: models.py:274 +msgid "user" +msgstr "użytkownik" + +#: models.py:276 +msgid "activation key" +msgstr "klucz aktywacyjny" + +#: models.py:282 +msgid "registration profile" +msgstr "profil rejestracji" + +#: models.py:283 +msgid "registration profiles" +msgstr "profile rejestracji" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "klucz aktywacyjny" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "klucz aktywacyjny" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "profil rejestracji" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "klucz aktywacyjny" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "hasło (ponownie)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "hasło" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "hasło" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "hasło" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "hasło" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "profil rejestracji" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "klucz aktywacyjny" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "nazwa użytkownika" + +#~ msgid "email address" +#~ msgstr "adres email" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "" +#~ "Nazwa użytkownika może zawierać tylko litery, cyfry i znaki podkreślenia" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "Ta nazwa użytkownika jest już zajęta. Wybierz inną." + +#~ msgid "You must type the same password each time" +#~ msgstr "Musisz wpisać to samo hasło w obu polach" diff --git a/pycons-site/registration/locale/pt/LC_MESSAGES/django.po b/pycons-site/registration/locale/pt/LC_MESSAGES/django.po new file mode 100644 index 0000000..e864668 --- /dev/null +++ b/pycons-site/registration/locale/pt/LC_MESSAGES/django.po @@ -0,0 +1,441 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2011-01-24 12:20+0000\n" +"Last-Translator: Nuno Mariz \n" +"Language-Team: Nuno Mariz \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Ativar utilizadores" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Reenviar emails de ativação" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Um utilizador com o mesmo nome já se encontra registado." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Eu li e concordo com as Condiçoes de Serviço" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Deverá concordar com as condições para se registar" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Este email já se encontra registado. Por favor forneça um email diferente." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"O registo com emails gratuitos é proibido. Por favor forneça um email " +"diferente." + +#: models.py:274 +msgid "user" +msgstr "utilizador" + +#: models.py:276 +msgid "activation key" +msgstr "chave de ativação" + +#: models.py:282 +msgid "registration profile" +msgstr "perfil de registo" + +#: models.py:283 +msgid "registration profiles" +msgstr "perfis de registo" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "chave de ativação" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Account activation failed." +msgstr "Reenviar emails de ativação" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "perfil de registo" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "chave de ativação" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "Password (again)" +msgid "Password changed" +msgstr "Password (novamente)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "Password" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "Password" +msgid "Set password" +msgstr "Password" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "Password" +msgid "Password reset" +msgstr "Password" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "Password" +msgid "Reset password" +msgstr "Password" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "perfil de registo" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Activation email sent" +msgstr "Reenviar emails de ativação" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Resend Activation Email" +msgstr "Reenviar emails de ativação" + +#~ msgid "Username" +#~ msgstr "Utilizador" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "Este valor apenas deverá conter letras, números e underscores." + +#~ msgid "Email address" +#~ msgstr "Email" + +#~ msgid "The two password fields didn't match." +#~ msgstr "As duas passwords não coincidem." diff --git a/pycons-site/registration/locale/pt_BR/LC_MESSAGES/django.po b/pycons-site/registration/locale/pt_BR/LC_MESSAGES/django.po new file mode 100644 index 0000000..49547c6 --- /dev/null +++ b/pycons-site/registration/locale/pt_BR/LC_MESSAGES/django.po @@ -0,0 +1,479 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2015-07-09 19:32-0300\n" +"Last-Translator: Paulo R \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Ativar usuários" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Reenviar emails de ativação" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "Email" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Já existe um usuário com este nome de usuário." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Eu lí e concordo com os Termos de Uso do serviço" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Você deve concordar com os termos para registrar-se" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Este endereço de email já está em uso. Por favor, informe um endereço de " +"email diferente." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Registrar-se com contas de email gratuitos está proibido. Por favor, informe " +"um endereço de email diferente." + +#: models.py:274 +msgid "user" +msgstr "usuário" + +#: models.py:276 +msgid "activation key" +msgstr "chave de ativação" + +#: models.py:282 +msgid "registration profile" +msgstr "profile de registro" + +#: models.py:283 +msgid "registration profiles" +msgstr "profiles de registro" + +#: templates/registration/activate.html:4 +msgid "Activation Failure" +msgstr "Falha na Ativação" + +#: templates/registration/activate.html:7 +msgid "Account activation failed." +msgstr "A ativação da conta falhou." + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "Conta Ativada" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "Sua conta agora está ativada" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "Você pode entrar." + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "Assim que um administrador do site ativar a sua conta você poderá " +"entrar." + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +msgid "registration" +msgstr "registro" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" +"\n" +" Você (ou alguém fingindo ser você) pediu para registrar uma conta em\n" +" %(site_name)s. Se não foi você, por favor ignore este email\n" +" e o seu endereço será removido dos nossos registros.\n" +" " + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" +"\n" +" Para ativar esta conta, por favor clique no seguinte link dentro dos " +"próximos\n" +" %(expiration_days)s dias:\n" +" " + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" +"\n" +" Atenciosamente,\n" +" Equipe do %(site_name)s\n" +" " + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" +"\n" +"Você (ou alguém fingindo ser você) pediu para registrar uma conta em\n" +"%(site_name)s. Se não foi você, por favor ignore este email\n" +"e o seu endereço será removido dos nossos registros.\n" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" +"\n" +"Para ativar esta conta, por favor clique no seguinte link dentro dos próximos\n" +"%(expiration_days)s dias:\n" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" +"\n" +"Atenciosamente,\n" +"Equipe do %(site_name)s\n" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +msgid "Account activation on" +msgstr "Ativação da conta em" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "Falha na Aprovação" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "Aprovação da Conta falhou." + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "Conta Aprovada" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "A conta do usuário agora está ativa." + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "aprovação do administrador" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" +"\n" +" Sua conta agora está aprovada. Você pode \n" +" " + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "entrar." + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" +"\n" +"Sua conta agora está aprovada. Você pode entrar usando o seguinte link\n" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" +"\n" +" Para aprovar isto, por favor\n" +" " + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "clique aqui" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" +"\n" +"O seguinte usuário (%(user)s) pediu para registrar uma conta em\n" +"%(site_name)s.\n" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" +"\n" +"Para aprovar isto, por favor clique no seguinte link.\n" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "Aprovação da conta em" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "Entrar" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "Esqueceu sua senha?" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "Restaure" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "Não é um membro?" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "Registrar" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "Saiu" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "Saiu com sucesso" + +#: templates/registration/password_change_done.html:4 +msgid "Password changed" +msgstr "Senha alterada" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "Senha alterada com sucesso" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +msgid "Change password" +msgstr "Alterar senha" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "Restauração de senha completa" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "Sua senha foi restaurada!" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "Agora você pode entrar" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "Confirmar restauração de senha" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "Digite a sua nova senha abaixo para restaurar a sua senha:" + +#: templates/registration/password_reset_confirm.html:18 +msgid "Set password" +msgstr "Definir senha" + +#: templates/registration/password_reset_done.html:4 +msgid "Password reset" +msgstr "Restauração de senha" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" +"\n" +" Nós te enviamos um email com um link para restaurar a sua senha. Por fafor " +"confira\n" +" o seu email e clique no link para continuar.\n" +" " + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "Saudações" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" +"\n" +"Você está recebendo este email por que você (ou alguém fingindo ser você)\n" +"pediu que a seua senha fosse resutarada no site do %(domain)s. Se você não \n" +"deseha restaurar a sua senha, por favor ignora esta mensagem.\n" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" +"\n" +"Para restaurar a sua senha, por favor clique no link a seguir, ou copie e " +"cole\n" +"no seu navegador:\n" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "Seu nome de usuário, caso você esqueceu:" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "Cumprimentos" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "Gerência" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +msgid "Reset password" +msgstr "Restaurar senha" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" +"\n" +" Esqueceu sua senha? Digite seu email no formulário abaixo e nós enviaremos " +"instruções para criar uma nova.\n" +" " + +#: templates/registration/registration_closed.html:4 +msgid "Registration is closed" +msgstr "Registro está fechado" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "Desculpe, mas o registro está fechando neste momento. Volte depois." + +#: templates/registration/registration_complete.html:4 +msgid "Activation email sent" +msgstr "E-mail de ativação enviado" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "Por favor verifique seu email para completar o processo de registro.ww" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "Registrar uma conta" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "Submeter" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "Ativação de Conta Reenviada" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" +"\n" +" Nós enviamos um email para %(email)s com mais instruções.\n" +" " + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "Reenviar Email de Ativação" + +#~ msgid "username" +#~ msgstr "usuário" + +#~ msgid "email address" +#~ msgstr "endereço de email" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "Nomes de usuário apenas podem conter letras, números, e underscore" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "Este nome de usuário já existe. Por favor, escolha outro." + +#~ msgid "You must type the same password each time" +#~ msgstr "Você deve escrever a mesma senha nos dois campos" diff --git a/pycons-site/registration/locale/ru/LC_MESSAGES/django.po b/pycons-site/registration/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000..4386957 --- /dev/null +++ b/pycons-site/registration/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,469 @@ +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Andrew Grigorev , 2015. +msgid "" +msgstr "" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-02-08 18:01+0300\n" +"PO-Revision-Date: 2015-09-09 00:39+0300\n" +"Last-Translator: Rostislav Grigorev \n" +"Language-Team: Russian <>\n" +"Language: ru_RU\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" +"X-Generator: Lokalize 2.0\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Активировать учетные записи" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Выслать ключи активации заново" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "E-mail" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Пользователь с таким именем уже существует." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Я прочитал Правила Использования и согласен с ними" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Для регистрации Вы должны согласиться с Правилами" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Этот адрес электронной почты уже используется. Пожалуйста, введите другой " +"адрес." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Регистрация с использованием свободных почтовых серверов запрещена. " +"Пожалуйста, введите другой адрес электронной почты." + +#: models.py:304 +msgid "user" +msgstr "пользователь" + +#: models.py:306 +msgid "activation key" +msgstr "ключ активации" + +#: models.py:312 +msgid "registration profile" +msgstr "карточка регистрации" + +#: models.py:313 +msgid "registration profiles" +msgstr "карточки регистрации" + +#: templates/registration/activate.html:4 +msgid "Activation Failure" +msgstr "Ошибка активации" + +#: templates/registration/activate.html:7 +msgid "Account activation failed." +msgstr "Активация аккаунта не удалась." + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "Учетная запись активирована" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "Ваша учетная запись теперь активирована." + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "Вы можете войти." + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" +"Как только администратор сайта активирует вашу учетную запись, вы сможете " +"войти в систему." + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +msgid "registration" +msgstr "регистрация" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" +"\n" +" Вы (или кто-то выдающий себя за вас) оставили заявку на регистрацию\n" +" учетной записи на %(site_name)s. Если это были не вы, то проигнорируйте " +"это сообщение, и ваш почтовый адрес будет удален из наших записей.\n" +" " + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" +"\n" +" Чтобы активировать эту учетную запись, перейдите по следующей ссылке в " +"течение %(expiration_days)s дней:\n" +" " + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" +"\n" +" С уважением,
\n" +" Команда %(site_name)s\n" +" " + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" +"\n" +"Вы (или кто-то выдающий себя за вас) оставили заявку на регистрацию\n" +"учетной записи на %(site_name)s. Если это были не вы, то проигнорируйте\n" +"это сообщение, и ваш почтовый адрес будет удален из наших записей.\n" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" +"\n" +"Чтобы активировать эту учетную запись, перейдите по следующей ссылке\n" +"в течение %(expiration_days)s дней:\n" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" +"\n" +"С уважением,\n" +"Команда %(site_name)s\n" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +msgid "Account activation on" +msgstr "Активация учетной записи на" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "Ошибка подтверждения" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "Подтвердить аккаунт не удалось." + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "Учетная запись подтверждена" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "Ваша учетная запись теперь одобрена." + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "одобрено администратором" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" +"\n" +" Ваша учетная запись теперь одобрена. Вы можете \n" +" " + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "войти" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" +"\n" +"Ваша учетная запись одобрена. Вы можете войти в систему, используя следующую " +"ссылку\n" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" +"\n" +" Пользователь (%(user)s) желает зарегистрировать учетную запись на\n" +" %(site_name)s.\n" +" " + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" +"\n" +" Чтобы это подтвердить, пожалуйста,\n" +" " + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "нажмите здесь" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" +"\n" +"Пользователь (%(user)s) желает зарегистрировать учетную запись на\n" +"%(site_name)s.\n" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" +"\n" +"Чтобы это подтвердить, пожалуйста, перейдите по следующей ссылке.\n" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "Подтверждение учетной записи на" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "Войти" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "Забыли свой пароль?" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "Сбросьте его" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "Нет учетной записи?" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "Зарегистрироваться" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "Выход" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "Вы успешно вышли из своей учетной записи" + +#: templates/registration/password_change_done.html:4 +msgid "Password changed" +msgstr "Пароль изменен" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "Пароль успешно изменен!" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +msgid "Change password" +msgstr "Изменить пароль" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "Сброс пароля завершен" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "Ваш пароль был сброшен!" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "Теперь вы можете войти" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "Подтверждение сброса пароля" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "Введите ваш новый пароль:" + +#: templates/registration/password_reset_confirm.html:18 +msgid "Set password" +msgstr "Установить пароль" + +#: templates/registration/password_reset_done.html:4 +msgid "Password reset" +msgstr "Сброс пароля" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" +"\n" +" Вам было отправлено письмо со ссылкой на сброс пароля. Проверьте\n" +" вашу почту и перейдите по указанной ссылке для продолжения.\n" +" " + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "Привет" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" +"\n" +"Вы получили это письмо потому что вы (или кто-то выдающий себя за вас)\n" +"запросил сброс пароля на сайте %(domain)s. Если вы не хотите сбрасывать\n" +"свой пароль, пожалуйста, проигнорируйте это сообщение.\n" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" +"\n" +"Чтобы сбросить пароль, перейдите по следующей ссылке:\n" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "Ваше имя пользователя, на случай если вы его забыли:" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "С уважением," + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr " " + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +msgid "Reset password" +msgstr "Сбросить пароль" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" +"\n" +" Забыли свой пароль? Введите свой адрес электронной почты и мы отправим " +"вам инструкции по установке нового пароля.\n" +" " + +#: templates/registration/registration_closed.html:4 +msgid "Registration is closed" +msgstr "Регистрация закрыта" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "Извините, но регистрация в данный момент закрыта. Попробуйте позже." + +#: templates/registration/registration_complete.html:4 +msgid "Activation email sent" +msgstr "Письмо активации отправлено" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "Проверьте свою электронную почту для завершения процесса регистрации." + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "Регистрация учетной записи" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "Отправить" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "Активация учетной записи" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" +"\n" +" Мы отправили электронное письмо с дальнейшими инструкциями на адрес " +"%(email)s.\n" +" " + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "Повторное письмо активации" diff --git a/pycons-site/registration/locale/sl/LC_MESSAGES/django.po b/pycons-site/registration/locale/sl/LC_MESSAGES/django.po new file mode 100644 index 0000000..e2462a8 --- /dev/null +++ b/pycons-site/registration/locale/sl/LC_MESSAGES/django.po @@ -0,0 +1,442 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: 0.8.1beta\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2011-11-05 21:20+0100\n" +"Last-Translator: Marko Mrdjenovic \n" +"Language-Team: Slovenian \n" +"Language: sl\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Slovenian\n" +"X-Poedit-Country: SLOVENIA\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Aktiviraj uporabnike" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Ponovno pošlji aktivacijsko e-pošto" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Uporabnik s tem uporabniškim imenom že obstaja." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Strinjam se s pogoji uporabe" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Za registracijo se morate strinjati s pogoji uporabe" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "E-naslov je že v uporabi, prosimo vnesite drugega." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Registracija ni mogoča z brezplačnimi e-naslovi. Prosimo vnesite drug e-" +"naslov." + +#: models.py:274 +msgid "user" +msgstr "Uporabnik" + +#: models.py:276 +msgid "activation key" +msgstr "Aktivacijski ključ" + +#: models.py:282 +msgid "registration profile" +msgstr "Registracijski profil" + +#: models.py:283 +msgid "registration profiles" +msgstr "Registracijski profili" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "Aktivacijski ključ" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Account activation failed." +msgstr "Ponovno pošlji aktivacijsko e-pošto" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "Registracijski profil" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "Aktivacijski ključ" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "Password (again)" +msgid "Password changed" +msgstr "Geslo (ponovno)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "Geslo" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "Password" +msgid "Set password" +msgstr "Geslo" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "Password" +msgid "Password reset" +msgstr "Geslo" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "Password" +msgid "Reset password" +msgstr "Geslo" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "Registracijski profil" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Activation email sent" +msgstr "Ponovno pošlji aktivacijsko e-pošto" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Resend Activation Email" +msgstr "Ponovno pošlji aktivacijsko e-pošto" + +#~ msgid "username" +#~ msgstr "uporabniško ime" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "Vrednost lahko vsebuje samo črke, cifre in podčrtaje." + +#~ msgid "Email address" +#~ msgstr "E-naslov" + +#~ msgid "The two password fields didn't match." +#~ msgstr "Polji z gesli se ne ujemata." diff --git a/pycons-site/registration/locale/sr/LC_MESSAGES/django.po b/pycons-site/registration/locale/sr/LC_MESSAGES/django.po new file mode 100644 index 0000000..461a1c5 --- /dev/null +++ b/pycons-site/registration/locale/sr/LC_MESSAGES/django.po @@ -0,0 +1,450 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: django-registration trunk\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2008-04-05 14:00+0100\n" +"Last-Translator: Nebojsa Djordjevic \n" +"Language-Team: \n" +"Language: \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" +"X-Poedit-Language: Serbian\n" +"X-Poedit-Country: YUGOSLAVIA\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "aktivacioni ključ" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Pročitao sam i slažem se sa uslovima korišćenja" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Morate se složiti sa uslovima korišćenja da bi ste se registrovali" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "" +"Ova e-mail adresa je već u upotrebi. Morate koristiti drugu e-mail adresu." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Registracija korišćenjem besplatnig e-mail adresa je zabranjena. Morate " +"uneti drugu e-mail adresu." + +#: models.py:274 +msgid "user" +msgstr "korisnik" + +#: models.py:276 +msgid "activation key" +msgstr "aktivacioni ključ" + +#: models.py:282 +msgid "registration profile" +msgstr "registracioni profil" + +#: models.py:283 +msgid "registration profiles" +msgstr "registracioni profili" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "aktivacioni ključ" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "aktivacioni ključ" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "registracioni profil" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "aktivacioni ključ" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "šifra (ponovo)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "šifra" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "šifra" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "šifra" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "šifra" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "registracioni profil" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "aktivacioni ključ" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "korisničko ime" + +#~ msgid "email address" +#~ msgstr "email adresa" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "" +#~ "Korisničko ime može da se sastoji samo od slova, brojeva i donje crte (\"_" +#~ "\")" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "Korisničko ime je već zauzeto. Izaberite drugo." + +#~ msgid "You must type the same password each time" +#~ msgstr "Unete šifre se ne slažu" diff --git a/pycons-site/registration/locale/sv/LC_MESSAGES/django.po b/pycons-site/registration/locale/sv/LC_MESSAGES/django.po new file mode 100644 index 0000000..9b96239 --- /dev/null +++ b/pycons-site/registration/locale/sv/LC_MESSAGES/django.po @@ -0,0 +1,442 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Emil Stenström \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "Aktiveringsnyckel" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Jag har läst och accepterar avtalet" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Du måste acceptera avtalet för att registrera dig" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Den e-postadressen är upptagen, använd an annan adress." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "Gratis e-postadresser är inte tillåtna, använd en annan adress." + +#: models.py:274 +msgid "user" +msgstr "Användare" + +#: models.py:276 +msgid "activation key" +msgstr "Aktiveringsnyckel" + +#: models.py:282 +msgid "registration profile" +msgstr "Profil" + +#: models.py:283 +msgid "registration profiles" +msgstr "Profiler" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "Aktiveringsnyckel" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "Aktiveringsnyckel" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "Profil" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "Aktiveringsnyckel" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "Lösenord (igen)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "Lösenord" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "Lösenord" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "Lösenord" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "Lösenord" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "Profil" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "Aktiveringsnyckel" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "Användarnamn" + +#~ msgid "email address" +#~ msgstr "E-postadress" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "Användarnamn får bara innehålla bokstäver, siffror och understreck" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "Det användarnamnet är upptaget. Prova ett annat." + +#~ msgid "You must type the same password each time" +#~ msgstr "Båda lösenord måste vara lika" diff --git a/pycons-site/registration/locale/th/LC_MESSAGES/django.po b/pycons-site/registration/locale/th/LC_MESSAGES/django.po new file mode 100644 index 0000000..17f7ff1 --- /dev/null +++ b/pycons-site/registration/locale/th/LC_MESSAGES/django.po @@ -0,0 +1,503 @@ +# Thai translation for django-registration. +# This file is distributed under the same license as the registration package. +# Phondanai Khanti , 2015. +# +msgid "" +msgstr "" +"Project-Id-Version: django-registration 0.3\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2015-11-05 15:37+0700\n" +"Last-Translator: Phondanai Khanti\n" +"Language-Team: \n" +"Language: th\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.8.1\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "เปิดใช้งานผู้ใช้" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "คีย์สำหรับเปิดใช้งาน" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "อีเมล" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "ชื่อผู้ใช้นี้ได้ถูกใช้ไปแล้ว" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "ฉันได้อ่านและเห็นด้วยกับข้อตกลงในการให้บริการ" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "คุณต้องยอมรับข้อตกลงในการลงทะเบียน" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "ที่อยู่อีเมลได้ถูกใช้งานไปแล้ว โปรดป้อนที่อยู่อีเมลอื่น" + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "การลงทะเบียนด้วยบริการฟรีอีเมลถูกระงับ โปรเลือกอีเมลอื่น" + +#: models.py:274 +msgid "user" +msgstr "ผู้ใช้" + +#: models.py:276 +msgid "activation key" +msgstr "คีย์สำหรับเปิดใช้งาน" + +#: models.py:282 +msgid "registration profile" +msgstr "โปรไฟล์การลงทะเบียน" + +#: models.py:283 +msgid "registration profiles" +msgstr "โปรไฟล์การลงทะเบียน" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "คีย์สำหรับเปิดใช้งาน" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "คีย์สำหรับเปิดใช้งาน" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "บัญชีผู้ใช้ได้เปิดใช้งานแล้ว" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "บัญชีผู้ใช้ของคุณได้เปิดใช้งานแล้ว" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "คุณสามารถเข้าสู่ระบบได้แล้ว" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "คุณจะสามารถเข้าสู่ระบบได้ ก็ต่อเมื่อผู้ดูแลระบบได้เปิดใช้บัญชีของคุณ" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "การลงทะเบียน" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" +"\n" +" คุณ (หรือบุคคลอื่น ที่แสดงตัวว่าเป็นคุณ) ได้ทำการสมัครสมาชิก" +"ที่\n" +" %(site_name)s ถ้าหากไม่ใช่คุณที่สมัคร โปรดเพิกเฉยต่ออีเมลฉบับนี้\n" +" และที่อยู่อีเมลของคุณจะถูกลบออกจากระบบของเราโดยอัตโนมัติ\n" +" " + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" +"\n" +" ในการเปิดใช้บัญชี กรุณาคลิกลิงค์ด้านล่าง" +"ภายใน\n" +" %(expiration_days)s วัน:\n" +" " + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" +"\n" +" ด้วยความนับถือ\n" +" จากผู้ดูแล %(site_name)s\n" +" " + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" +"\n" +"คุณ (หรือบุคคลอื่น ที่แสดงตัวว่าเป็นคุณ) ได้ทำการสมัครสมาชิกที่\n" +"%(site_name)s ถ้าหากไม่ใช่คุณที่สมัคร โปรดเพิกเฉยต่ออีเมลฉบับนี้\n" +"และที่อยู่อีเมลของคุณจะถูกลบออกจากระบบของเราโดยอัตโนมัติ\n" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" +"\n" +"ในการเปิดใช้บัญชี กรุณาคลิกลิงค์ด้านล่างภายใน\n" +"%(expiration_days)s วัน:\n" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" +"\n" +"ด้วยความนับถือ\n" +"จากผู้ดูแล %(site_name)s\n" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "บัญชีได้เปิดใช้งานมื่อ" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "การอนุมัติเกิดข้อผิดพลาด" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "การอนุมัติบัญชีผู้ใช้เกิดข้อผิดพลาด" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "บัญชีได้ถูกอนุมัติแล้ว" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "บัญชีผู้ใช้ได้ถูกอนุมัติแล้ว" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "อนุมัติโดยผู้ดูแลระบบ" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" +"\n" +" บัญชีของคุณได้ถูกอนุมัติแล้ว คุณสามารถ\n" +" " + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "เข้าสู่ระบบ" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" +"\n" +"บัญชีของคุณได้ถูกอนุมัติแล้ว คุณสามารถเข้าสู่ระบบโดยใช้ลิงค์ด้านล่างนี้\n" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" +"\n" +" ผู้ใช้ (%(user)s) ได้ทำการลงทะเบียนบัญชี ที่\n" +" %(site_name)s\n" +" " + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" +"\n" +" หากต้องการอนุมัติ กรุณา\n" +" " + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "คลิก ที่นี่" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" +"\n" +"ผู้ใช้ (%(user)s) ได้ทำการลงทะเบียนบัญชี ที่\n" +"%(site_name)s\n" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" +"\n" +"หากต้องการอนุมัติ กรุณาคลิกลิงค์ด้านล่างนี้\n" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "การอนุมัติบัญชี ที่" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "เข้าสู่ระบบ" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "ลืมรหัสผ่าน?" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "รีเซ็ต" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "ยังไม่ได้เป็นสมาชิก?" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "ลงทะเบียน" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "ออกจากระบบ" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "ออกจากระบบสำเร็จแล้ว" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "รหัสผ่าน (อีกครั้ง)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "รหัสผ่านได้ถูกเปลี่ยนแล้ว!" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "เปลี่ยนรหัสผ่าน" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "รีเซ็ตรหัสผ่านสำเร็จ" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "รหัสผ่านของคุณได้ถูกรีเซ็ตแล้ว!" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "คุณอาจจะ เข้าสู่ระบบ" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "ยืนยันการรีเซ็ตรหัสผ่าน" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "ตั้งรหัสผ่านใหม่ด้านล่าง เพื่อทำการรีเซ็ตรหัสผ่าน:" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "ตั้งรหัสผ่าน" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "รีเซ็ตรหัสผ่าน" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" +"\n" +" เราได้ทำการส่งอีเมลไปหาคุณ พร้อมกับลิงค์เพื่อทำการรีเซ็ตรหัสผ่าน กรุณา " +"ตรวจสอบ\n" +" อีเมลของคุณ และ คลิกลิงค์ที่ดำเนินการต่อ\n" +" " + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "สวัสดี" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" +"\n" +"คุณได้รับอีเมลฉบับนี้ เพราะ คุณ (หรือบุคคลอื่นที่อาจจะเป็นคุณ)\n" +"ได้ทำการร้องขอการรีเซ็ตรหัสผ่านบนเว็บ %(domain) sหากคณไม่ต้องการรีเซ็ต\n" +"รหัสผ่านของคุณ โปรดเพิกเฉยต่อข้อความนี้\n" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" +"\n" +"ในการรีเซ็ตรหัสผ่าน โปรดคลิกลิงค์ด้านล่างหรือคัดลอก " +"ลิงค์\n" +"แล้วนำไปเปิดที่โปรแกรมท่องเว็บของคุณ:\n" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "ชื่อผู้ใช้ ในกรณีที่คุณลืม:" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "ด้วยความเคารพ" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "การจัดการ" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "รีเซ็ตรหัสผ่าน" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" +"\n" +" ลืมรหัสผ่าน? ป้อนอีเมลของคุณในฟอร์มด้านล่างนี้ ทางเราจะส่ง " +"วิธีการรีเซ็ตให้กับคุณ\n" +" " + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "โปรไฟล์การลงทะเบียน" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "ขออภัย แต่การลงทะเบียนได้ปิดชั่วคราว โปรดกลับมาอีกครั้ง" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "อีเมลสำหรับการเปิดใช้บัญชีได้ถูกส่งไปแล้ว" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "โปรดตรวจสอบอีเมลของคุณเพื่อ ดำเนินการเปิดใช้บัญชีของคุณ" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "ลงทะเบียนบัญชีใหม่" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "ตกลง" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "ส่งอีเมลการเปิดใช้บัญชีอีกครั้ง" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" +"\n" +" เราได้ส่งอีเมล ไปยัง %(email)s พร้อมกับกระบวนการ\n" +" " + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "ส่งอีเมลการเปิดใช้บัญชีอีกครั้ง" + +#~ msgid "username" +#~ msgstr "ชื่อผู้ใช้" + +#~ msgid "email address" +#~ msgstr "ที่อยู่อีเมล" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "ชื่อผู้ใช้สามารถมีเฉพาะตัวอักษรตัวเลขและขีดล่างเท่านั้น" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "ชื่อผู้ใช้นี้ถูกใช้แล้ว กรุณาเลือกชื่อผู้ใช้อื่น" + +#~ msgid "You must type the same password each time" +#~ msgstr "คุณต้องพิมพ์รหัสผ่านเดียวกัน" diff --git a/pycons-site/registration/locale/tr_TR/LC_MESSAGES/django.po b/pycons-site/registration/locale/tr_TR/LC_MESSAGES/django.po new file mode 100644 index 0000000..6c5460b --- /dev/null +++ b/pycons-site/registration/locale/tr_TR/LC_MESSAGES/django.po @@ -0,0 +1,441 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: RECEP KIRMIZI \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "Kullanıcıları aktive et" + +#: admin.py:44 +msgid "Re-send activation emails" +msgstr "Aktivasyon e-postalarını yeniden gönder" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "Bu kullanıcı adına sahip bir kullanıcı bulunmakta." + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "Anlaşma kurallarını okudum ve kabul ediyorum." + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "Kayıt olmak için kuralları kabul etmelisiniz." + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "Bu eposta adresi kullanımda. Lütfen farklı bir eposta adresi veriniz." + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "" +"Ücretsiz eposta adresleri ile kayıt kabul edilmemektedir. Lütfen farklı bir " +"eposta adresi veriniz." + +#: models.py:274 +msgid "user" +msgstr "kullanıcı" + +#: models.py:276 +msgid "activation key" +msgstr "aktivasyon anahtarı" + +#: models.py:282 +msgid "registration profile" +msgstr "kayıt profili" + +#: models.py:283 +msgid "registration profiles" +msgstr "kayıt profilleri" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "aktivasyon anahtarı" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Account activation failed." +msgstr "Aktivasyon e-postalarını yeniden gönder" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "kayıt profili" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "aktivasyon anahtarı" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "Password (again)" +msgid "Password changed" +msgstr "Parola (tekrar)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "Password" +msgid "Change password" +msgstr "Parola" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "Password" +msgid "Set password" +msgstr "Parola" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "Password" +msgid "Password reset" +msgstr "Parola" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "Password" +msgid "Reset password" +msgstr "Parola" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "kayıt profili" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Activation email sent" +msgstr "Aktivasyon e-postalarını yeniden gönder" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +#, fuzzy +#| msgid "Re-send activation emails" +msgid "Resend Activation Email" +msgstr "Aktivasyon e-postalarını yeniden gönder" + +#~ msgid "Username" +#~ msgstr "Kullanıcı adı" + +#~ msgid "This value must contain only letters, numbers and underscores." +#~ msgstr "Bu alan sadece harfler, numaralar ve alt çizgiler barındırabilir." + +#~ msgid "Email address" +#~ msgstr "Eposta adresi" + +#~ msgid "The two password fields didn't match." +#~ msgstr "İki parola birbiri ile uyuşmadı." diff --git a/pycons-site/registration/locale/zh_CN/LC_MESSAGES/django.po b/pycons-site/registration/locale/zh_CN/LC_MESSAGES/django.po new file mode 100644 index 0000000..5127cd1 --- /dev/null +++ b/pycons-site/registration/locale/zh_CN/LC_MESSAGES/django.po @@ -0,0 +1,441 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2008-03-20 23:22+0800\n" +"Last-Translator: hutuworm \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "激活密钥" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "我已阅读并同意该服务条款" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "您必须同意注册条款" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "该 Email 地址已有人使用,请提供一个另外的 Email 地址。" + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "禁止使用免费 Email 地址注册,请提供一个另外的 Email 地址。" + +#: models.py:274 +msgid "user" +msgstr "用户" + +#: models.py:276 +msgid "activation key" +msgstr "激活密钥" + +#: models.py:282 +msgid "registration profile" +msgstr "注册信息" + +#: models.py:283 +msgid "registration profiles" +msgstr "注册信息" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "激活密钥" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "激活密钥" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "注册信息" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "激活密钥" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "密码(重复)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "密码" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "密码" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "密码" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "密码" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "注册信息" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "激活密钥" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "用户名" + +#~ msgid "email address" +#~ msgstr "Email 地址" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "用户名只能包含字母、数字和下划线" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "该用户名已被占用,请另选一个。" + +#~ msgid "You must type the same password each time" +#~ msgstr "您必须输入两遍同样的密码" diff --git a/pycons-site/registration/locale/zh_TW/LC_MESSAGES/django.po b/pycons-site/registration/locale/zh_TW/LC_MESSAGES/django.po new file mode 100644 index 0000000..30b84fc --- /dev/null +++ b/pycons-site/registration/locale/zh_TW/LC_MESSAGES/django.po @@ -0,0 +1,441 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-10-17 11:22-0400\n" +"PO-Revision-Date: 2008-03-20 23:22+0800\n" +"Last-Translator: hutuworm \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: admin.py:26 +msgid "Activate users" +msgstr "" + +#: admin.py:44 +#, fuzzy +#| msgid "activation key" +msgid "Re-send activation emails" +msgstr "激活密鑰" + +#: forms.py:36 forms.py:114 +msgid "E-mail" +msgstr "" + +#: forms.py:52 +msgid "A user with that username already exists." +msgstr "" + +#: forms.py:64 +msgid "I have read and agree to the Terms of Service" +msgstr "我已閱讀並同意該服務條款" + +#: forms.py:65 +msgid "You must agree to the terms to register" +msgstr "您必須同意注冊條款" + +#: forms.py:81 +msgid "" +"This email address is already in use. Please supply a different email " +"address." +msgstr "該 Email 地址已有人使用,請提供一個另外的 Email 地址。" + +#: forms.py:108 +msgid "" +"Registration using free email addresses is prohibited. Please supply a " +"different email address." +msgstr "禁止使用免費 Email 地址注冊,請提供一個另外的 Email 地址。" + +#: models.py:274 +msgid "user" +msgstr "用戶" + +#: models.py:276 +msgid "activation key" +msgstr "激活密鑰" + +#: models.py:282 +msgid "registration profile" +msgstr "注冊信息" + +#: models.py:283 +msgid "registration profiles" +msgstr "注冊信息" + +#: templates/registration/activate.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation Failure" +msgstr "激活密鑰" + +#: templates/registration/activate.html:7 +#, fuzzy +#| msgid "activation key" +msgid "Account activation failed." +msgstr "激活密鑰" + +#: templates/registration/activation_complete.html:4 +#: templates/registration/activation_complete_admin_pending.html:4 +msgid "Account Activated" +msgstr "" + +#: templates/registration/activation_complete.html:8 +#: templates/registration/activation_complete_admin_pending.html:8 +msgid "Your account is now activated." +msgstr "" + +#: templates/registration/activation_complete.html:10 +msgid "You can log in." +msgstr "" + +#: templates/registration/activation_complete_admin_pending.html:10 +msgid "Once a site administrator activates your account you can login." +msgstr "" + +#: templates/registration/activation_email.html:6 +#: templates/registration/admin_approve_email.html:6 +#, fuzzy +#| msgid "registration profile" +msgid "registration" +msgstr "注冊信息" + +#: templates/registration/activation_email.html:11 +#, python-format +msgid "" +"\n" +" You (or someone pretending to be you) have asked to register an account " +"at\n" +" %(site_name)s. If this wasn't you, please ignore this email\n" +" and your address will be removed from our records.\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:18 +#, python-format +msgid "" +"\n" +" To activate this account, please click the following link within the " +"next\n" +" %(expiration_days)s days:\n" +" " +msgstr "" + +#: templates/registration/activation_email.html:30 +#: templates/registration/admin_approve_email.html:23 +#, python-format +msgid "" +"\n" +" Sincerely,\n" +" %(site_name)s Management\n" +" " +msgstr "" + +#: templates/registration/activation_email.txt:2 +#, python-format +msgid "" +"\n" +"You (or someone pretending to be you) have asked to register an account at\n" +"%(site_name)s. If this wasn't you, please ignore this email\n" +"and your address will be removed from our records.\n" +msgstr "" + +#: templates/registration/activation_email.txt:7 +#, python-format +msgid "" +"\n" +"To activate this account, please click the following link within the next\n" +"%(expiration_days)s days:\n" +msgstr "" + +#: templates/registration/activation_email.txt:14 +#, python-format +msgid "" +"\n" +"Sincerely,\n" +"%(site_name)s Management\n" +msgstr "" + +#: templates/registration/activation_email_subject.txt:1 +#: templates/registration/admin_approve_complete_email_subject.txt:1 +#, fuzzy +#| msgid "activation key" +msgid "Account activation on" +msgstr "激活密鑰" + +#: templates/registration/admin_approve.html:4 +msgid "Approval Failure" +msgstr "" + +#: templates/registration/admin_approve.html:7 +msgid "Account Approval failed." +msgstr "" + +#: templates/registration/admin_approve_complete.html:4 +msgid "Account Approved" +msgstr "" + +#: templates/registration/admin_approve_complete.html:8 +msgid "The user's account is now approved." +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:6 +msgid "admin approval" +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:11 +msgid "" +"\n" +" Your account is now approved. You can \n" +" " +msgstr "" + +#: templates/registration/admin_approve_complete_email.html:14 +msgid "log in." +msgstr "" + +#: templates/registration/admin_approve_complete_email.txt:2 +msgid "" +"\n" +"Your account is now approved. You can log in using the following link\n" +msgstr "" + +#: templates/registration/admin_approve_email.html:11 +#, python-format +msgid "" +"\n" +" The following user (%(user)s) has asked to register an account at\n" +" %(site_name)s.\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:17 +msgid "" +"\n" +" To approve this, please\n" +" " +msgstr "" + +#: templates/registration/admin_approve_email.html:20 +msgid "click here" +msgstr "" + +#: templates/registration/admin_approve_email.txt:2 +#, python-format +msgid "" +"\n" +"The following user (%(user)s) has asked to register an account at\n" +"%(site_name)s.\n" +msgstr "" + +#: templates/registration/admin_approve_email.txt:6 +msgid "" +"\n" +"To approve this, please click the following link.\n" +msgstr "" + +#: templates/registration/admin_approve_email_subject.txt:1 +msgid "Account approval on" +msgstr "" + +#: templates/registration/login.html:4 templates/registration/login.html:10 +msgid "Log in" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Forgot your password?" +msgstr "" + +#: templates/registration/login.html:14 +msgid "Reset it" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Not a member?" +msgstr "" + +#: templates/registration/login.html:15 +msgid "Register" +msgstr "" + +#: templates/registration/logout.html:4 +msgid "Logged out" +msgstr "" + +#: templates/registration/logout.html:7 +msgid "Successfully logged out" +msgstr "" + +#: templates/registration/password_change_done.html:4 +#, fuzzy +#| msgid "password (again)" +msgid "Password changed" +msgstr "密碼(重復)" + +#: templates/registration/password_change_done.html:7 +msgid "Password successfully changed!" +msgstr "" + +#: templates/registration/password_change_form.html:4 +#: templates/registration/password_change_form.html:10 +#, fuzzy +#| msgid "password" +msgid "Change password" +msgstr "密碼" + +#: templates/registration/password_reset_complete.html:4 +msgid "Password reset complete" +msgstr "" + +#: templates/registration/password_reset_complete.html:8 +msgid "Your password has been reset!" +msgstr "" + +#: templates/registration/password_reset_complete.html:9 +#, python-format +msgid "You may now log in" +msgstr "" + +#: templates/registration/password_reset_confirm.html:10 +msgid "Confirm password reset" +msgstr "" + +#: templates/registration/password_reset_confirm.html:14 +msgid "Enter your new password below to reset your password:" +msgstr "" + +#: templates/registration/password_reset_confirm.html:18 +#, fuzzy +#| msgid "password" +msgid "Set password" +msgstr "密碼" + +#: templates/registration/password_reset_done.html:4 +#, fuzzy +#| msgid "password" +msgid "Password reset" +msgstr "密碼" + +#: templates/registration/password_reset_done.html:8 +msgid "" +"\n" +" We have sent you an email with a link to reset your password. Please " +"check\n" +" your email and click the link to continue.\n" +" " +msgstr "" + +#: templates/registration/password_reset_email.html:3 +msgid "Greetings" +msgstr "" + +#: templates/registration/password_reset_email.html:5 +#, python-format +msgid "" +"\n" +"You are receiving this email because you (or someone pretending to be you)\n" +"requested that your password be reset on the %(domain)s site. If you do not\n" +"wish to reset your password, please ignore this message.\n" +msgstr "" + +#: templates/registration/password_reset_email.html:11 +msgid "" +"\n" +"To reset your password, please click the following link, or copy and paste " +"it\n" +"into your web browser:\n" +msgstr "" + +#: templates/registration/password_reset_email.html:20 +msgid "Your username, in case you've forgotten:" +msgstr "" + +#: templates/registration/password_reset_email.html:23 +msgid "Best regards" +msgstr "" + +#: templates/registration/password_reset_email.html:24 +msgid "Management" +msgstr "" + +#: templates/registration/password_reset_form.html:4 +#: templates/registration/password_reset_form.html:15 +#, fuzzy +#| msgid "password" +msgid "Reset password" +msgstr "密碼" + +#: templates/registration/password_reset_form.html:8 +msgid "" +"\n" +" Forgot your password? Enter your email in the form below and we'll send " +"you instructions for creating a new one.\n" +" " +msgstr "" + +#: templates/registration/registration_closed.html:4 +#, fuzzy +#| msgid "registration profile" +msgid "Registration is closed" +msgstr "注冊信息" + +#: templates/registration/registration_closed.html:7 +msgid "Sorry, but registration is closed at this moment. Come back later." +msgstr "" + +#: templates/registration/registration_complete.html:4 +#, fuzzy +#| msgid "activation key" +msgid "Activation email sent" +msgstr "激活密鑰" + +#: templates/registration/registration_complete.html:7 +msgid "Please check your email to complete the registration process." +msgstr "" + +#: templates/registration/registration_form.html:4 +msgid "Register for an account" +msgstr "" + +#: templates/registration/registration_form.html:10 +#: templates/registration/resend_activation_form.html:10 +msgid "Submit" +msgstr "" + +#: templates/registration/resend_activation_complete.html:4 +msgid "Account Activation Resent" +msgstr "" + +#: templates/registration/resend_activation_complete.html:8 +#, python-format +msgid "" +"\n" +" We have sent an email to %(email)s with further instructions.\n" +" " +msgstr "" + +#: templates/registration/resend_activation_form.html:4 +msgid "Resend Activation Email" +msgstr "" + +#~ msgid "username" +#~ msgstr "用戶名" + +#~ msgid "email address" +#~ msgstr "Email 地址" + +#~ msgid "Usernames can only contain letters, numbers and underscores" +#~ msgstr "用戶名只能包含字母、數字和下劃線" + +#~ msgid "This username is already taken. Please choose another." +#~ msgstr "該用戶名已被佔用,請另選一個。" + +#~ msgid "You must type the same password each time" +#~ msgstr "您必須輸入兩遍同樣的密碼" diff --git a/pycons-site/registration/management/__init__.py b/pycons-site/registration/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/registration/management/commands/__init__.py b/pycons-site/registration/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/registration/management/commands/cleanupregistration.py b/pycons-site/registration/management/commands/cleanupregistration.py new file mode 100644 index 0000000..55f6704 --- /dev/null +++ b/pycons-site/registration/management/commands/cleanupregistration.py @@ -0,0 +1,24 @@ +""" +A management command which deletes expired accounts (e.g., +accounts which signed up but never activated) from the database. + +Calls ``RegistrationProfile.objects.delete_expired_users()``, which +contains the actual logic for determining which accounts are deleted. + +""" + +from django.core.management.base import BaseCommand + +from ...models import RegistrationProfile + + +class Command(BaseCommand): + help = "Delete expired user registrations from the database" + + def handle(self, *args, **options): + self.stdout.write('Running cleanupregistration.') + deleted_count = RegistrationProfile.objects.delete_expired_users() + if deleted_count == 0: + self.stdout.write('cleanupregistration completed. There is no user has to be deleted.') + else: + self.stdout.write('cleanupregistration completed. Deleted user count=%d' % deleted_count) diff --git a/pycons-site/registration/middleware.py b/pycons-site/registration/middleware.py new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/pycons-site/registration/middleware.py @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/pycons-site/registration/migrations/0001_initial.py b/pycons-site/registration/migrations/0001_initial.py new file mode 100644 index 0000000..775508b --- /dev/null +++ b/pycons-site/registration/migrations/0001_initial.py @@ -0,0 +1,69 @@ +# Generated by Django 3.2 on 2022-10-03 19:03 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone +import django_countries.fields +import django_extensions.db.fields +import hitcount.views +import markdownx.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='Profile', + fields=[ + ('profile_image', models.ImageField(blank=True, default='speakers/speaker.png', upload_to='speakers/')), + ('name', models.CharField(default='', max_length=100)), + ('surname', models.CharField(default='', max_length=100)), + ('link_to_speaker_image', models.URLField(blank=True, default='', help_text="A link to your professional profile picture to be used when making speaker's announcements")), + ('profession', models.CharField(default='', help_text="Speaker's profession. eg. Software Developer", max_length=200, null=True)), + ('organization', models.CharField(blank=True, default='', help_text='Organization/Institution', max_length=200, null=True)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='auth.user')), + ('biography', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - Give us a brief biography about yourself')), + ('city', models.CharField(default='', help_text='City the speaker is from eg. Accra', max_length=100)), + ('country', django_countries.fields.CountryField(default='GH', max_length=2)), + ('contact_number', models.CharField(default='', help_text='Please include your country code (233).', max_length=50, null=True)), + ('website', models.URLField(blank=True, help_text='Your website/blog URL.', null=True)), + ('twitter_handle', models.CharField(blank=True, default='', help_text="Please enter only the user name eg.'mawy_7' ", max_length=100, null=True)), + ('github_username', models.CharField(blank=True, default='', help_text="Please enter only the user name eg.'mawy_7' ", max_length=100, null=True)), + ('linkedin', models.CharField(blank=True, default='', help_text="Please enter only the user name eg.'/mawy_7' ", max_length=100, null=True)), + ('date_created', models.DateTimeField(default=django.utils.timezone.now)), + ('updated', models.DateTimeField(auto_now=True)), + ('is_visible', models.BooleanField(default=False)), + ('published_date', models.DateField(blank=True, null=True)), + ('slug', django_extensions.db.fields.AutoSlugField(blank=True, editable=False, populate_from='surname')), + ], + bases=(models.Model, hitcount.views.HitCountMixin), + ), + migrations.CreateModel( + name='RegistrationProfile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('activation_key', models.CharField(max_length=64, verbose_name='activation key')), + ('activated', models.BooleanField(default=False)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')), + ], + options={ + 'verbose_name': 'registration profile', + 'verbose_name_plural': 'registration profiles', + }, + ), + migrations.CreateModel( + name='SupervisedRegistrationProfile', + fields=[ + ('registrationprofile_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registration.registrationprofile')), + ], + bases=('registration.registrationprofile',), + ), + ] diff --git a/pycons-site/registration/migrations/0002_alter_profile_slug.py b/pycons-site/registration/migrations/0002_alter_profile_slug.py new file mode 100644 index 0000000..d9da291 --- /dev/null +++ b/pycons-site/registration/migrations/0002_alter_profile_slug.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2 on 2022-10-04 07:25 + +from django.db import migrations +import django_extensions.db.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('registration', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='profile', + name='slug', + field=django_extensions.db.fields.AutoSlugField(blank=True, editable=False, populate_from='name'), + ), + ] diff --git a/pycons-site/registration/migrations/0003_alter_registrationprofile_id.py b/pycons-site/registration/migrations/0003_alter_registrationprofile_id.py new file mode 100644 index 0000000..20a859a --- /dev/null +++ b/pycons-site/registration/migrations/0003_alter_registrationprofile_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-02-28 14:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('registration', '0002_alter_profile_slug'), + ] + + operations = [ + migrations.AlterField( + model_name='registrationprofile', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/pycons-site/registration/migrations/0004_profile_profile_id_alter_profile_user.py b/pycons-site/registration/migrations/0004_profile_profile_id_alter_profile_user.py new file mode 100644 index 0000000..a1e9dcd --- /dev/null +++ b/pycons-site/registration/migrations/0004_profile_profile_id_alter_profile_user.py @@ -0,0 +1,27 @@ +# Generated by Django 5.0.2 on 2024-02-29 23:00 + +import django.db.models.deletion +import hashid_field.field +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('registration', '0003_alter_registrationprofile_id'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='profile', + name='profile_id', + field=hashid_field.field.HashidAutoField(alphabet='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', default=None, min_length=7, prefix='', primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='profile', + name='user', + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='user_profile', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/pycons-site/registration/migrations/0005_alter_profile_profile_image.py b/pycons-site/registration/migrations/0005_alter_profile_profile_image.py new file mode 100644 index 0000000..1ddca6c --- /dev/null +++ b/pycons-site/registration/migrations/0005_alter_profile_profile_image.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-03-01 00:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('registration', '0004_profile_profile_id_alter_profile_user'), + ] + + operations = [ + migrations.AlterField( + model_name='profile', + name='profile_image', + field=models.ImageField(blank=True, default='', upload_to='speakers/'), + ), + ] diff --git a/pycons-site/registration/migrations/0006_alter_profile_linkedin.py b/pycons-site/registration/migrations/0006_alter_profile_linkedin.py new file mode 100644 index 0000000..54585f3 --- /dev/null +++ b/pycons-site/registration/migrations/0006_alter_profile_linkedin.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-03-01 15:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('registration', '0005_alter_profile_profile_image'), + ] + + operations = [ + migrations.AlterField( + model_name='profile', + name='linkedin', + field=models.CharField(blank=True, default='', help_text='Please your LinkedIn link ', max_length=100, null=True), + ), + ] diff --git a/pycons-site/registration/migrations/__init__.py b/pycons-site/registration/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/registration/mixins.py b/pycons-site/registration/mixins.py new file mode 100644 index 0000000..514d8b3 --- /dev/null +++ b/pycons-site/registration/mixins.py @@ -0,0 +1,23 @@ +from django.core.exceptions import PermissionDenied + +class EditOwnProfileMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnProfileMixin, self).get_object(*args, **kwargs) + self.check_permission(obj) + return obj + def check_permission(self, obj): + if hasattr(obj, 'user'): + obj = obj.user + if obj == self.request.user: + return + else: + raise PermissionDenied() + +class EditOwnLoginMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnLoginMixin, self).get_object(*args, **kwargs) + if obj.email == self.request.user.email: + return obj + else: + raise PermissionDenied + diff --git a/pycons-site/registration/models.py b/pycons-site/registration/models.py new file mode 100644 index 0000000..0c7e3ed --- /dev/null +++ b/pycons-site/registration/models.py @@ -0,0 +1,825 @@ +from avatar.models import Avatar +import datetime +import hashlib +import logging +import re +import string +import warnings +import magic + +from django.utils import timezone +from django.apps import apps +from django.conf import settings +from django.core.exceptions import ImproperlyConfigured +from django.core.exceptions import MultipleObjectsReturned +from django.core.exceptions import ObjectDoesNotExist +from django.core.mail import EmailMultiAlternatives +from django.db import models +from django.db import transaction +from django.template import TemplateDoesNotExist +from django.template.loader import render_to_string +from django.utils.crypto import get_random_string +from django.utils.module_loading import import_string +from django.utils.timezone import now as datetime_now + +from .users import UserModel +from .users import UserModelString +from .utils import _ + +from taggit.managers import TaggableManager +from django.utils.deconstruct import deconstructible +from django.template.defaultfilters import filesizeformat +from markdownx.models import MarkdownxField + +logger = logging.getLogger(__name__) + +# Adding some backwards compatibility for SHA1 +# The 40 probably should be removed later on +SHA256_RE = re.compile('^[a-f0-9]{40,64}$') + +from django_extensions.db.fields import AutoSlugField +from django_slugify_processor.text import slugify + +from imagekit.models import ProcessedImageField +from imagekit.processors import ResizeToFit +from six import python_2_unicode_compatible + +# Included by me (3rd Parties and more) +from django.utils.encoding import smart_str +from django.contrib.auth import hashers + +from PIL import Image, ImageDraw +cfgDefaultImageResample = Image.BICUBIC # Image.LANCZOS + +from django.utils.translation import gettext_lazy as _ +from django.urls import reverse +from django.contrib.auth.models import User +from django.db.models.signals import post_save +from django.dispatch import receiver +from io import BytesIO +import logging +from django.core.files.base import ContentFile +from django_thumbs.fields import ImageThumbsField + +from simple_history.models import HistoricalRecords +import os +from django.utils import timezone +from django.core.validators import MaxLengthValidator + +from avatar.models import AvatarField + +from django_countries.fields import CountryField + +from hitcount.models import HitCountMixin +from hitcount.settings import MODEL_HITCOUNT +from hitcount.models import HitCount +from hitcount.views import HitCountMixin + +from django.contrib.contenttypes.fields import GenericRelation +from django_recaptcha.fields import ReCaptchaField + +from hashids import Hashids +from django.conf import settings +from hashid_field import HashidAutoField +from hashid_field import HashidField + + + +def get_from_email(site=None): + """ + Return the email address by which mail is sent. + If the `REGISTRATION_USE_SITE_EMAIL` setting is set, the `Site` object will + provide the domain and the REGISTRATION_SITE_USER_EMAIL will provide the + username. Otherwise the `REGISTRATION_DEFAULT_FROM_EMAIL` or + `DEFAULT_FROM_EMAIL` settings are used. + """ + if getattr(settings, 'REGISTRATION_USE_SITE_EMAIL', False): + user_email = getattr(settings, 'REGISTRATION_SITE_USER_EMAIL', None) + if not user_email: + raise ImproperlyConfigured(( + 'REGISTRATION_SITE_USER_EMAIL must be set when using ' + 'REGISTRATION_USE_SITE_EMAIL.')) + Site = apps.get_model('sites', 'Site') + site = site or Site.objects.get_current() + from_email = '{}@{}'.format(user_email, site.domain) + else: + from_email = getattr(settings, 'REGISTRATION_DEFAULT_FROM_EMAIL', + settings.DEFAULT_FROM_EMAIL) + return from_email + + +def send_email(addresses_to, ctx_dict, subject_template, body_template, + body_html_template): + """ + Function that sends an email + """ + + prefix = getattr(settings, 'REGISTRATION_EMAIL_SUBJECT_PREFIX', '') + subject = prefix + render_to_string(subject_template, ctx_dict) + # Email subject *must not* contain newlines + subject = ''.join(subject.splitlines()) + from_email = get_from_email(ctx_dict.get('site')) + message_txt = render_to_string(body_template, + ctx_dict) + + email_message = EmailMultiAlternatives(subject, message_txt, + from_email, addresses_to) + + if getattr(settings, 'REGISTRATION_EMAIL_HTML', True): + try: + message_html = render_to_string( + body_html_template, ctx_dict) + except TemplateDoesNotExist: + pass + else: + email_message.attach_alternative(message_html, 'text/html') + + email_message.send() + + +class RegistrationManager(models.Manager): + """ + Custom manager for the ``RegistrationProfile`` model. + + The methods defined here provide shortcuts for account creation + and activation (including generation and emailing of activation + keys), and for cleaning out expired inactive accounts. + + """ + + def _activate(self, profile, site, get_profile): + """ + Activate the ``RegistrationProfile`` given as argument. + User is able to login, as ``is_active`` is set to ``True`` + """ + user = profile.user + user.is_active = True + profile.activated = True + + with transaction.atomic(): + user.save() + profile.save() + if get_profile: + return profile + else: + return user + + def activate_user(self, activation_key, site, get_profile=False): + """ + Validate an activation key and activate the corresponding ``User`` if + valid, returns a tuple of (``User``, ``activated``). The activated flag + indicates if the user was newly activated or an error occurred. + + If the key is valid and has not expired, return the (``User``, + ``True``) after activating. + + If the key is not valid or has expired, return (``User`` or ``False``, + ``False``). + + If the key is valid but the ``User`` is already active, + return (``User``, ``False``). + + If the key is valid but the ``User`` is inactive, return (``User``, + ``False``). + + To prevent reactivation of an account which has been + deactivated by site administrators, ``RegistrationProfile.activated`` + is set to ``True`` after successful activation. + + """ + # Make sure the key we're trying conforms to the pattern of a + # SHA256 hash; if it doesn't, no point trying to look it up in + # the database. + # The or statement is used + if SHA256_RE.search(activation_key): + try: + profile = self.get(activation_key=activation_key) + except self.model.DoesNotExist: + # This is an actual activation failure as the activation + # key does not exist. It is *not* the scenario where an + # already activated User reuses an activation key. + return (False, False) + + if profile.activated: + # The User has already activated and is trying to activate + # again. If the User is active, return the User. Else, + # return False as the User has been deactivated by a site + # administrator. + return (profile.user, False) + + if not profile.activation_key_expired(): + return (self._activate(profile, site, get_profile), True) + + return (False, False) + + def create_inactive_user(self, site, new_user=None, send_email=True, + request=None, profile_info={}, **user_info): + """ + Create a new, inactive ``User``, generate a + ``RegistrationProfile`` and email its activation key to the + ``User``, returning the new ``User``. + + By default, an activation email will be sent to the new + user. To disable this, pass ``send_email=False``. + Additionally, if email is sent and ``request`` is supplied, + it will be passed to the email template. + + """ + if new_user is None: + password = user_info.pop('password') + new_user = UserModel()(**user_info) + new_user.set_password(password) + new_user.is_active = False + + # Since we calculate the RegistrationProfile expiration from this date, + # we want to ensure that it is current + new_user.date_joined = datetime_now() + + with transaction.atomic(): + new_user.save() + registration_profile = self.create_profile( + new_user, **profile_info) + + # send email only if desired and transaction succeeds + if send_email: + transaction.on_commit( + lambda: registration_profile.send_activation_email( + site, request) + ) + + return new_user + + def create_profile(self, user, **profile_info): + """ + Create a ``RegistrationProfile`` for a given + ``User``, and return the ``RegistrationProfile``. + + The activation key for the ``RegistrationProfile`` will be a + SHA256 hash, generated from a secure random string. + + """ + profile = self.model(user=user, **profile_info) + + if 'activation_key' not in profile_info: + profile.create_new_activation_key(save=False) + + profile.save() + + return profile + + def resend_activation_mail(self, email, site, request=None): + """ + Resets activation key for the user and resends activation email. + """ + try: + profile = self.get(user__email__iexact=email) + except ObjectDoesNotExist: + return False + except MultipleObjectsReturned: + return False + + if profile.activated or profile.activation_key_expired(): + return False + + profile.create_new_activation_key() + profile.send_activation_email(site, request) + + return True + + def delete_expired_users(self): + """ + Remove expired instances of ``RegistrationProfile`` and their + associated ``User``s. + + Accounts to be deleted are identified by searching for instances of + ``RegistrationProfile`` with expired activation keys and an + ``activated`` field that is set to ``False``. If these conditions are + met both the ``RegistrationProfile`` and the ``User`` objects will be + deleted. + + It is recommended that this method be executed regularly as + part of your routine site maintenance; this application + provides a custom management command which will call this + method, accessible as ``manage.py cleanupregistration``. + + Regularly clearing out accounts which have never been + activated serves two useful purposes: + + 1. It alleviates the occasional need to reset a + ``RegistrationProfile`` and/or re-send an activation email + when a user does not receive or does not act upon the + initial activation email; since the account will be + deleted, the user will be able to simply re-register and + receive a new activation key. + + 2. It prevents the possibility of a malicious user registering + one or more accounts and never activating them (thus + denying the use of those usernames to anyone else); since + those accounts will be deleted, the usernames will become + available for use again. + + If you have a troublesome ``User`` and wish to disable their + account while keeping it in the database, simply delete the + associated ``RegistrationProfile``; an inactive ``User`` which + does not have an associated ``RegistrationProfile`` will not + be deleted. + + """ + profiles = self.filter( + models.Q(user__is_active=False) | models.Q(user=None), + activated=False, + ) + deleted_count = 0 + for profile in profiles: + try: + if profile.activation_key_expired(): + user = profile.user + logger.warning('Deleting expired Registration profile {} and user {}.'.format(profile, user)) + profile.delete() + user.delete() + deleted_count += 1 + except UserModel().DoesNotExist: + logger.warning('Deleting expired Registration profile {}'.format(profile)) + profile.delete() + deleted_count += 1 + return deleted_count + + +class RegistrationProfile(models.Model): + """ + A simple profile which stores an activation key for use during + user account registration. + + Generally, you will not want to interact directly with instances + of this model; the provided manager includes methods + for creating and activating new accounts, as well as for cleaning + out accounts which have never been activated. + + While it is possible to use this model as the value of the + ``AUTH_PROFILE_MODULE`` setting, it's not recommended that you do + so. This model's sole purpose is to store data temporarily during + account registration and activation. + + """ + default_auto_field = 'django.db.models.AutoField' + + user = models.OneToOneField( + UserModelString(), + on_delete=models.CASCADE, + verbose_name=_('user'), + ) + activation_key = models.CharField(_('activation key'), max_length=64) + activated = models.BooleanField(default=False) + + objects = RegistrationManager() + + class Meta: + verbose_name = _('registration profile') + verbose_name_plural = _('registration profiles') + + def __str__(self): + return "Registration information for %s" % self.user + + def create_new_activation_key(self, save=True): + """ + Create a new activation key for the user + """ + random_string = get_random_string( + length=32, allowed_chars=string.printable) + self.activation_key = hashlib.sha256( + random_string.encode()).hexdigest() + + if save: + self.save() + + return self.activation_key + + def activation_key_expired(self): + """ + Determine whether this ``RegistrationProfile``'s activation + key has expired, returning a boolean -- ``True`` if the key + has expired. + + Key expiration is determined by a two-step process: + + 1. If the user has already activated, ``self.activated`` will + be ``True``. Re-activating is not permitted, and so this + method returns ``True`` in this case. + + 2. Otherwise, the date the user signed up is incremented by + the number of days specified in the setting + ``ACCOUNT_ACTIVATION_DAYS`` (which should be the number of + days after signup during which a user is allowed to + activate their account); if the result is less than or + equal to the current date, the key has expired and this + method returns ``True``. + + """ + max_expiry_days = datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS) + expiration_date = self.user.date_joined + max_expiry_days + return self.activated or expiration_date <= datetime_now() + + def send_activation_email(self, site, request=None): + """ + Send an activation email to the user associated with this + ``RegistrationProfile``. + + The activation email will use the following templates, + which can be overridden by setting ACTIVATION_EMAIL_SUBJECT, + ACTIVATION_EMAIL_BODY, and ACTIVATION_EMAIL_HTML appropriately: + + ``registration/activation_email_subject.txt`` + This template will be used for the subject line of the + email. Because it is used as the subject line of an email, + this template's output **must** be only a single line of + text; output longer than one line will be forcibly joined + into only a single line. + + ``registration/activation_email.txt`` + This template will be used for the text body of the email. + + ``registration/activation_email.html`` + This template will be used for the html body of the email. + + These templates will each receive the following context + variables: + + ``user`` + The new user account + + ``activation_key`` + The activation key for the new account. + + ``expiration_days`` + The number of days remaining during which the account may + be activated. + + ``site`` + An object representing the site on which the user + registered; depending on whether ``django.contrib.sites`` + is installed, this may be an instance of either + ``django.contrib.sites.models.Site`` (if the sites + application is installed) or + ``django.contrib.sites.requests.RequestSite`` (if + not). Consult the documentation for the Django sites + framework for details regarding these objects' interfaces. + + ``request`` + Optional Django's ``HttpRequest`` object from view. + If supplied will be passed to the template for better + flexibility via ``RequestContext``. + """ + activation_email_subject = getattr(settings, 'ACTIVATION_EMAIL_SUBJECT', + 'registration/activation_email_subject.txt') + activation_email_body = getattr(settings, 'ACTIVATION_EMAIL_BODY', + 'registration/activation_email.txt') + activation_email_html = getattr(settings, 'ACTIVATION_EMAIL_HTML', + 'registration/activation_email.html') + + ctx_dict = { + 'user': self.user, + 'activation_key': self.activation_key, + 'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS, + 'site': site, + } + prefix = getattr(settings, 'REGISTRATION_EMAIL_SUBJECT_PREFIX', '') + subject = prefix + render_to_string( + activation_email_subject, ctx_dict, request=request + ) + + # Email subject *must not* contain newlines + subject = ''.join(subject.splitlines()) + from_email = get_from_email(site) + message_txt = render_to_string(activation_email_body, + ctx_dict, request=request) + + email_message = EmailMultiAlternatives(subject, message_txt, + from_email, [self.user.email]) + + if getattr(settings, 'REGISTRATION_EMAIL_HTML', True): + try: + message_html = render_to_string( + activation_email_html, ctx_dict, request=request) + except TemplateDoesNotExist: + pass + else: + email_message.attach_alternative(message_html, 'text/html') + + email_message.send() + + +class SupervisedRegistrationManager(RegistrationManager): + + def activation_key_expired(self): + """ + Determine whether this ``RegistrationProfile``'s activation + key has expired, returning a boolean -- ``True`` if the key + has expired. + + Key expiration is determined by a two-step process: + + 1. If the user has already activated, ``self.activated`` and + `self.user.is_active`` will be ``True``. Re-activating is not + permitted, and so this method returns ``True`` in this case. + + 2. Otherwise, the date the user signed up is incremented by the number + of days specified in the setting ``ACCOUNT_ACTIVATION_DAYS`` (which + should be the number of days after signup during which a user is + allowed to activate their account); if the result is less than or equal + to the current date, the key has expired and this method returns + ``True``. + """ + expiration_date = datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS) + # A user is only considered activated when the entire registration + # process is completed (i.e. an admin has approved the account) + is_activated = self.activated and self.user.is_active + return (is_activated or self.user.date_joined + expiration_date <= datetime_now()) + + def _activate(self, profile, site, get_profile): + """ + Activate the ``SupervisedRegistrationProfile`` given as argument. + + Send an email to the site administrators to approve the user. + + User is not able to login yet, as ``is_active`` is not yet ``True`` + """ + + if not profile.user.is_active and not profile.activated: + self.send_admin_approve_email(profile.user, site) + + # do not set ``User.is_active`` as True. This will be set + # when a site administrator approves this account. + profile.activated = True + profile.save() + if get_profile: + return profile + else: + return profile.user + + def admin_approve_user(self, profile_id, site, get_profile=False, request=None): + """ + Approve the ``SupervisedRegistrationProfile`` + object with the given ``profile_id``. + + If the id is valid, return the ``User`` + after approving. + + If the id is not valid, return ``False``. + + If the id is valid but the ``User`` is already active, + return ``User``. + + If the id is valid but the ``SupervisedRegistrationProfile`` + object is not activated, return ``False``. + """ + try: + profile = SupervisedRegistrationProfile.objects.get(id=profile_id) + if profile.activated: + if profile.user.is_active: + return profile.user + + # If the user has not activated their profile the admin should + # not be able to approve his account (at least not following + # this process) + if profile.activated: + profile.user.is_active = True + else: + return False + + profile.user.save() + profile.send_admin_approve_complete_email(site, request) + + if get_profile: + return profile + else: + return profile.user + except self.model.DoesNotExist: + return False + + def send_admin_approve_email(self, user, site, request=None): + """ + Send an approval email to the site administrators to + approve this user. + + The approval email will use the following templates, + which can be overridden by setting APPROVAL_EMAIL_SUBJECT, + APPROVAL_EMAIL_BODY, and APPROVAL_EMAIL_HTML appropriately: + + ``registration/admin_approve_email_subject.txt`` + This template will be used for the subject line of the + email. Because it is used as the subject line of an email, + this template's output **must** be only a single line of + text; output longer than one line will be forcibly joined + into only a single line. + + ``registration/admin_approve_email.txt`` + This template will be used for the text body of the email. + + ``registration/admin_approve_email.html`` + This template will be used for the html body of the email. + + These templates will each receive the following context + variables: + + ``user`` + The new user account + + ``profile_id`` + The id of the associated``SupervisedRegistrationProfile`` + object. + + ``site`` + An object representing the site on which the user + registered; depending on whether ``django.contrib.sites`` + is installed, this may be an instance of either + ``django.contrib.sites.models.Site`` (if the sites + application is installed) or + ``django.contrib.sites.requests.RequestSite`` (if + not). Consult the documentation for the Django sites + framework for details regarding these objects' interfaces. + + ``request`` + Optional Django's ``HttpRequest`` object from view. + If supplied will be passed to the template for better + flexibility via ``RequestContext``. + """ + + admin_approve_email_subject = getattr( + settings, + 'ADMIN_APPROVAL_EMAIL_SUBJECT', + 'registration/admin_approve_email_subject.txt' + ) + admin_approve_email_body = getattr( + settings, + 'ADMIN_APPROVAL_EMAIL_BODY', + 'registration/admin_approve_email.txt' + ) + admin_approve_email_html = getattr( + settings, + 'ADMIN_APPROVAL_EMAIL_HTML', + 'registration/admin_approve_email.html' + ) + + ctx_dict = { + 'user': user, + 'profile_id': user.registrationprofile.id, + 'site': site, + } + registration_admins = getattr(settings, 'REGISTRATION_ADMINS', None) + if isinstance(registration_admins, str): # We have a getter + admins_getter = import_string(registration_admins) + admins = admins_getter() + else: + admins = registration_admins or getattr(settings, 'ADMINS', None) + if not registration_admins: + warnings.warn('No registration admin defined in' + ' settings.REGISTRATION_ADMINS.' + ' Using settings.ADMINS for the admin approval', + UserWarning) + if not admins: + raise ImproperlyConfigured( + 'Using the admin_approval registration backend' + ' requires at least one admin in settings.ADMINS' + ' or settings.REGISTRATION_ADMINS') + + admins = [admin[1] for admin in admins] + send_email( + admins, ctx_dict, admin_approve_email_subject, + admin_approve_email_body, admin_approve_email_html + ) + + +class SupervisedRegistrationProfile(RegistrationProfile): + + # Same model as ``RegistrationProfile``, just a different + # Manager to implement the extra functionality required + # in admin approval + objects = SupervisedRegistrationManager() + + def send_admin_approve_complete_email(self, site, request=None): + """ + Send an "approval is complete" email to the user associated with this + ``SupervisedRegistrationProfile``. + + The email will use the following templates, + which can be overridden by settings APPROVAL_COMPLETE_EMAIL_SUBJECT, + APPROVAL_COMPLETE_EMAIL_BODY, and APPROVAL_COMPLETE_EMAIL_HTML appropriately: + + ``registration/admin_approve_complete_email_subject.txt`` + This template will be used for the subject line of the + email. Because it is used as the subject line of an email, + this template's output **must** be only a single line of + text; output longer than one line will be forcibly joined + into only a single line. + + ``registration/admin_approve_complete_email.txt`` + This template will be used for the text body of the email. + + ``registration/admin_approve_complete_email.html`` + This template will be used for the text body of the email. + + These templates will each receive the following context + variables: + + ``user`` + The new user account + + ``site`` + An object representing the site on which the user + registered; depending on whether ``django.contrib.sites`` + is installed, this may be an instance of either + ``django.contrib.sites.models.Site`` (if the sites + application is installed) or + ``django.contrib.sites.requests.RequestSite`` (if + not). Consult the documentation for the Django sites + framework for details regarding these objects' interfaces. + + ``request`` + Optional Django's ``HttpRequest`` object from view. + If supplied will be passed to the template for better + flexibility via ``RequestContext``. + """ + admin_approve_complete_email_subject = getattr( + settings, 'APPROVAL_COMPLETE_EMAIL_SUBJECT', + 'registration/admin_approve_complete_email_subject.txt') + admin_approve_complete_email_body = getattr( + settings, 'APPROVAL_COMPLETE_EMAIL_BODY', + 'registration/admin_approve_complete_email.txt') + admin_approve_complete_email_html = getattr( + settings, 'APPROVAL_COMPLETE_EMAIL_HTML', + 'registration/admin_approve_complete_email.html') + + ctx_dict = { + 'user': self.user, + 'site': site, + } + send_email( + [self.user.email], ctx_dict, + admin_approve_complete_email_subject, + admin_approve_complete_email_body, + admin_approve_complete_email_html + ) + + + + + +class Profile(models.Model, HitCountMixin): + profile_id = HashidAutoField(primary_key=True, salt=f"user_profile{settings.HASHID_FIELD_SALT}", default=None) + profile_image = models.ImageField(upload_to='speakers/', default="", blank=True) + name = models.CharField(max_length=100, default='', blank=False) + surname = models.CharField(max_length=100, default='', blank=False) + link_to_speaker_image = models.URLField(default="", blank=True, help_text="A link to your professional profile picture to be used when making speaker's announcements") + profession = models.CharField(max_length=200, null=True, default="", blank=False, help_text="Speaker's profession. eg. Software Developer") + organization = models.CharField(max_length=200, null=True, default="", blank=True, help_text="Organization/Institution") + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="user_profile") + biography = MarkdownxField(help_text="[Supports Markdown] - Give us a brief biography about yourself", default="", blank=False) + city = models.CharField(max_length=100, default="", blank=False, help_text="City the speaker is from eg. Accra") + country = CountryField(default="GH", blank=False, blank_label='(select country)') + contact_number = models.CharField(max_length=50, help_text="Please include your country code (233).", default="", blank=True, null=True) + website = models.URLField(max_length=200, help_text="Your website/blog URL.", null=True, blank=True) + twitter_handle = models.CharField(max_length=100, null=True, help_text="Please enter only the user name eg.'mawy_7'", default="", blank=True) + github_username = models.CharField(max_length=100, null=True, help_text="Please enter only the user name eg.'mawy_7'", default="", blank=True) + linkedin = models.CharField(max_length=100, null=True, help_text="Please your LinkedIn link", default="", blank=True) + date_created = models.DateTimeField(default=timezone.now) + updated = models.DateTimeField(auto_now=True) + is_visible = models.BooleanField(default=False) + is_a_sponsor_or_keynote_speaker = models.BooleanField(default=False) + published_date = models.DateField(blank=True, null=True) + slug = AutoSlugField( + populate_from='name', + unique=True, + slugify_function=slugify + ) + hit_count_generic = GenericRelation( + HitCount, object_id_field='object_pk', + related_query_name='hit_count_generic_relation') + + def __str__(self): + return str(self.name) + + def save(self, *args, **kwargs): + if not self.slug or Profile.objects.filter(slug=self.slug).exists(): + self.slug = self.create_unique_slug() + super(Profile, self).save(*args, **kwargs) + + def create_unique_slug(self): + original_slug = slugify(self.name) + queryset = Profile.objects.filter(slug__startswith=original_slug).order_by('profile_id') + slug = original_slug + counter = 1 + while queryset.filter(slug=slug).exists(): + slug = f"{original_slug}-{counter}" + counter += 1 + if counter > 100: + raise RuntimeError(f'max slug attempts for {original_slug} exceeded (100)') + return slug + + def get_full_name(self): + return f"{self.name} {self.surname}" + + + + diff --git a/pycons-site/registration/signals.py b/pycons-site/registration/signals.py new file mode 100644 index 0000000..158cb8c --- /dev/null +++ b/pycons-site/registration/signals.py @@ -0,0 +1,26 @@ +from django.conf import settings +from django.contrib.auth import get_backends +from django.contrib.auth import login +from django.dispatch import Signal + +# An admin has approved a user's account +user_approved = Signal() + +# A new user has registered. +user_registered = Signal() + +# A user has activated his or her account. +user_activated = Signal() + + +def login_user(sender, user, request, **kwargs): + """ Automatically authenticate the user when activated """ + backend = get_backends()[0] # Hack to bypass `authenticate()`. + user.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__) + login(request, user) + request.session['REGISTRATION_AUTO_LOGIN'] = True + request.session.modified = True + + +if getattr(settings, 'REGISTRATION_AUTO_LOGIN', False): + user_activated.connect(login_user) diff --git a/pycons-site/registration/templatetags/__init__.py b/pycons-site/registration/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/registration/templatetags/profile_tags.py b/pycons-site/registration/templatetags/profile_tags.py new file mode 100644 index 0000000..1ffb27e --- /dev/null +++ b/pycons-site/registration/templatetags/profile_tags.py @@ -0,0 +1,18 @@ +from django import template + +register = template.Library() + +from registration.models import Profile + + +@register.inclusion_tag('profiles/profilepic_nav.html') +def show_nav_pics(): + show_nav_pic = Profile.objects.all() + return {'show_nav_pic': show_nav_pic} + + +@register.inclusion_tag('profiles/profilepic_side.html') +def show_side_pics(): + show_side_pic = Profile.objects.all() + return {'show_side_pic': show_side_pic} + \ No newline at end of file diff --git a/pycons-site/registration/tests/__init__.py b/pycons-site/registration/tests/__init__.py new file mode 100644 index 0000000..5a33f37 --- /dev/null +++ b/pycons-site/registration/tests/__init__.py @@ -0,0 +1,7 @@ +from registration import admin +from registration.backends.default import urls + + +def test(): + assert admin + assert urls diff --git a/pycons-site/registration/tests/admin_actions.py b/pycons-site/registration/tests/admin_actions.py new file mode 100644 index 0000000..6ed1a65 --- /dev/null +++ b/pycons-site/registration/tests/admin_actions.py @@ -0,0 +1,68 @@ +from django.contrib.admin import helpers +from django.core import mail +from django.test import TestCase +from django.test.client import Client +from django.test.utils import override_settings +from django.urls import reverse + +from registration.models import RegistrationProfile +from registration.users import UserModel + + +@override_settings(ACCOUNT_ACTIVATION_DAYS=7, + REGISTRATION_DEFAULT_FROM_EMAIL='registration@email.com', + REGISTRATION_EMAIL_HTML=True, + DEFAULT_FROM_EMAIL='django@email.com') +class AdminCustomActionsTestCase(TestCase): + """ + Test the available admin custom actions + """ + + def setUp(self): + self.client = Client() + admin_user = UserModel().objects.create_superuser( + 'admin', 'admin@test.com', 'admin') + self.client.login(username=admin_user.get_username(), password=admin_user) + + self.user_info = {'username': 'alice', + 'password': 'swordfish', + 'email': 'alice@example.com'} + + def test_activate_users(self): + """ + Test the admin custom command 'activate users' + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = RegistrationProfile.objects.create_profile(new_user) + + self.assertFalse(profile.activated) + + registrationprofile_list = reverse( + 'admin:registration_registrationprofile_changelist') + post_data = { + 'action': 'activate_users', + helpers.ACTION_CHECKBOX_NAME: [profile.pk], + } + self.client.post(registrationprofile_list, post_data, follow=True) + + profile = RegistrationProfile.objects.get(user=new_user) + self.assertTrue(profile.activated) + + def test_resend_activation_email(self): + """ + Test the admin custom command 'resend activation email' + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = RegistrationProfile.objects.create_profile(new_user) + + registrationprofile_list = reverse( + 'admin:registration_registrationprofile_changelist') + post_data = { + 'action': 'resend_activation_email', + helpers.ACTION_CHECKBOX_NAME: [profile.pk], + } + self.client.post(registrationprofile_list, post_data, follow=True) + + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].to, [self.user_info['email']]) diff --git a/pycons-site/registration/tests/admin_approval_backend.py b/pycons-site/registration/tests/admin_approval_backend.py new file mode 100644 index 0000000..ddb7b82 --- /dev/null +++ b/pycons-site/registration/tests/admin_approval_backend.py @@ -0,0 +1,117 @@ +from django.conf import settings +from django.core import mail +from django.test.utils import override_settings +from django.urls import reverse + +from .default_backend import DefaultBackendViewTests + +from registration.backends.admin_approval.views import RegistrationView +from registration.models import SupervisedRegistrationProfile +from registration.users import UserModel + + +def get_registration_admins(): + """ + Mock function for testing the admin getter functionality + + """ + return [ + ("Functional admin 1", "func_admin1@fakemail.com"), + ("Functional admin 2", "func_admin2@fakemail.com") + ] + + +@override_settings(ROOT_URLCONF='test_app.urls_admin_approval') +class AdminApprovalBackendViewTests(DefaultBackendViewTests): + """ + Test the admin_approval registration backend. + + Running these tests successfully will require two templates to be + created for the sending of activation emails; details on these + templates and their contexts may be found in the documentation for + the default backend. + + """ + + registration_profile = SupervisedRegistrationProfile + + registration_view = RegistrationView + + def test_approval(self): + """ + Approval of an account functions properly. + + """ + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + + profile = self.registration_profile.objects.get(user__username='bob') + self.assertFalse(profile.user.is_active) + + resp = self.client.get( + reverse('registration_activate', + args=(), + kwargs={'activation_key': profile.activation_key})) + + admin_user = UserModel().objects.create_superuser('admin', 'admin@test.com', 'admin') + self.client.login(username=admin_user.get_username(), password=admin_user) + + resp = self.client.get( + reverse('registration_admin_approve', + args=(), + kwargs={'profile_id': profile.id})) + profile.user.refresh_from_db() + self.assertTrue(profile.user.is_active) + self.assertRedirects(resp, reverse('registration_approve_complete')) + + @override_settings( + REGISTRATION_ADMINS=[ + ("The admin", "admin_alpha@fakemail.com"), + ("The other admin", "admin_two@fakemail.com") + ] + ) + def test_admins_when_is_list(self): + """ + Admins are pulled from the REGISTRATION_ADMINS list setting + """ + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + + profile = self.registration_profile.objects.get(user__username='bob') + + resp = self.client.get( + reverse('registration_activate', + args=(), + kwargs={'activation_key': profile.activation_key})) + self.assertRedirects(resp, reverse('registration_activation_complete')) + admins_mail = mail.outbox[1] + self.assertEqual(admins_mail.to, [to[1] for to in settings.REGISTRATION_ADMINS]) + + @override_settings( + REGISTRATION_ADMINS="registration.tests.admin_approval_backend.get_registration_admins" + ) + def test_admins_when_is_getter(self): + """ + Admins are pulled from the REGISTRATION_ADMINS string setting + """ + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + + profile = self.registration_profile.objects.get(user__username='bob') + + resp = self.client.get( + reverse('registration_activate', + args=(), + kwargs={'activation_key': profile.activation_key})) + self.assertRedirects(resp, reverse('registration_activation_complete')) + admins_mail = mail.outbox[1] + self.assertEqual(admins_mail.to, [to[1] for to in get_registration_admins()]) diff --git a/pycons-site/registration/tests/default_backend.py b/pycons-site/registration/tests/default_backend.py new file mode 100644 index 0000000..3a2a22f --- /dev/null +++ b/pycons-site/registration/tests/default_backend.py @@ -0,0 +1,254 @@ +import datetime + +from django.conf import settings +from django.contrib.auth.models import AnonymousUser +from django.core import mail +from django.db import DatabaseError +from django.test import TransactionTestCase +from django.test.client import RequestFactory +from django.test.utils import override_settings +from django.urls import reverse +from mock import patch + +from registration.backends.default.views import RegistrationView +from registration.forms import RegistrationForm +from registration.models import RegistrationProfile +from registration.users import UserModel + + +@override_settings(ROOT_URLCONF='test_app.urls_default', + ACCOUNT_ACTIVATION_DAYS=7) +class DefaultBackendViewTests(TransactionTestCase): + """ + Test the default registration backend. + + Running these tests successfully will require two templates to be + created for the sending of activation emails; details on these + templates and their contexts may be found in the documentation for + the default backend. + + """ + + registration_profile = RegistrationProfile + + registration_view = RegistrationView + + @override_settings(REGISTRATION_OPEN=True) + def test_registration_open(self): + """ + The setting ``REGISTRATION_OPEN`` appropriately controls + whether registration is permitted. + + """ + resp = self.client.get(reverse('registration_register')) + self.assertEqual(200, resp.status_code) + + @override_settings(REGISTRATION_OPEN=False) + def test_registration_closed(self): + + # Now all attempts to hit the register view should redirect to + # the 'registration is closed' message. + resp = self.client.get(reverse('registration_register')) + self.assertRedirects(resp, reverse('registration_disallowed')) + + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + self.assertRedirects(resp, reverse('registration_disallowed')) + + def test_registration_get(self): + """ + HTTP ``GET`` to the registration view uses the appropriate + template and populates a registration form into the context. + + """ + resp = self.client.get(reverse('registration_register')) + self.assertEqual(200, resp.status_code) + self.assertTemplateUsed(resp, + 'registration/registration_form.html') + self.assertIsInstance(resp.context['form'], RegistrationForm) + + def test_registration(self): + """ + Registration creates a new inactive account and a new profile + with activation key, populates the correct account data and + sends an activation email. + + """ + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + self.assertRedirects(resp, reverse('registration_complete')) + + new_user = UserModel().objects.get(username='bob') + + self.assertTrue(new_user.check_password('secret')) + self.assertEqual(new_user.email, 'bob@example.com') + + # New user must not be active. + self.assertFalse(new_user.is_active) + + # A registration profile was created, and an activation email + # was sent. + self.assertEqual(self.registration_profile.objects.count(), 1) + self.assertEqual(len(mail.outbox), 1) + + def test_registration_no_email(self): + """ + Overridden Registration view does not send an activation email if the + associated class variable is set to ``False`` + + """ + class RegistrationNoEmailView(self.registration_view): + SEND_ACTIVATION_EMAIL = False + + request_factory = RequestFactory() + view = RegistrationNoEmailView.as_view() + request = request_factory.post('/', data={ + 'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + request.user = AnonymousUser() + view(request) + + UserModel().objects.get(username='bob') + # A registration profile was created, and no activation email was sent. + self.assertEqual(self.registration_profile.objects.count(), 1) + self.assertEqual(len(mail.outbox), 0) + + @override_settings( + INSTALLED_APPS=('django.contrib.auth', 'registration',) + ) + def test_registration_no_sites(self): + """ + Registration still functions properly when + ``django.contrib.sites`` is not installed; the fallback will + be a ``RequestSite`` instance. + + """ + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + self.assertEqual(302, resp.status_code) + + new_user = UserModel().objects.get(username='bob') + + self.assertTrue(new_user.check_password('secret')) + self.assertEqual(new_user.email, 'bob@example.com') + + self.assertFalse(new_user.is_active) + + self.assertEqual(self.registration_profile.objects.count(), 1) + self.assertEqual(len(mail.outbox), 1) + + def test_registration_failure(self): + """ + Registering with invalid data fails. + + """ + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'notsecret'}) + self.assertEqual(200, resp.status_code) + self.assertFalse(resp.context['form'].is_valid()) + self.assertEqual(0, len(mail.outbox)) + + @patch('registration.models.RegistrationManager.create_inactive_user') + def test_registration_exception(self, create_inactive_user): + """ + User is not created beforehand if an exception occurred at + creating registration profile. + """ + create_inactive_user.side_effect = DatabaseError() + valid_data = {'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'} + with self.assertRaises(DatabaseError): + self.client.post(reverse('registration_register'), + data=valid_data) + assert not UserModel().objects.filter(username='bob').exists() + + def test_activation(self): + """ + Activation of an account functions properly. + + """ + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + + profile = self.registration_profile.objects.get(user__username='bob') + + resp = self.client.get( + reverse('registration_activate', + args=(), + kwargs={'activation_key': profile.activation_key})) + self.assertRedirects(resp, reverse('registration_activation_complete')) + + def test_activation_expired(self): + """ + An expired account can't be activated. + + """ + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + + profile = self.registration_profile.objects.get(user__username='bob') + user = profile.user + user.date_joined -= datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS) + user.save() + + resp = self.client.get( + reverse('registration_activate', + args=(), + kwargs={'activation_key': profile.activation_key})) + + self.assertEqual(200, resp.status_code) + self.assertTemplateUsed(resp, 'registration/activate.html') + user = UserModel().objects.get(username='bob') + self.assertFalse(user.is_active) + + def test_resend_activation(self): + """ + Resend activation functions properly. + + """ + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + + profile = self.registration_profile.objects.get(user__username='bob') + + resp = self.client.post(reverse('registration_resend_activation'), + data={'email': profile.user.email}) + self.assertTemplateUsed(resp, + 'registration/resend_activation_complete.html') + self.assertEqual(resp.context['email'], profile.user.email) + + def test_resend_activation_invalid_email(self): + """ + Calling resend with an invalid email shows the same template. + + """ + resp = self.client.post(reverse('registration_resend_activation'), + data={'email': 'invalid@example.com'}) + self.assertTemplateUsed(resp, + 'registration/resend_activation_complete.html') diff --git a/pycons-site/registration/tests/forms.py b/pycons-site/registration/tests/forms.py new file mode 100644 index 0000000..f3c07fc --- /dev/null +++ b/pycons-site/registration/tests/forms.py @@ -0,0 +1,152 @@ +import django +from django.test import TestCase + +from registration import forms +from registration.users import UserModel + + +class RegistrationFormTests(TestCase): + """ + Test the default registration forms. + + """ + + def test_registration_form(self): + """ + Test that ``RegistrationForm`` enforces username constraints + and matching passwords. + + """ + # Create a user so we can verify that duplicate usernames aren't + # permitted. + UserModel().objects.create_user('alice', 'alice@example.com', 'secret') + bad_username_error = ( + 'Enter a valid username. This value may contain only letters, ' + 'numbers, and @/./+/-/_ characters.' + ) + password_didnt_match_error = "The two password fields didn't match." + if django.VERSION >= (3, 0): + password_didnt_match_error = "The two password fields didn’t match." + + invalid_data_dicts = [ + # Non-alphanumeric username. + {'data': {'username': 'foo/bar', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo'}, + 'error': ('username', [bad_username_error])}, + # Already-existing username. + {'data': {'username': 'alice', + 'email': 'alice@example.com', + 'password1': 'secret', + 'password2': 'secret'}, + 'error': ('username', ["A user with that username already exists."])}, + # Mismatched passwords. + {'data': {'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'bar'}, + 'error': ('password2', [password_didnt_match_error])}, + ] + + for invalid_dict in invalid_data_dicts: + form = forms.RegistrationForm(data=invalid_dict['data']) + self.assertFalse(form.is_valid()) + self.assertEqual(form.errors[invalid_dict['error'][0]], + invalid_dict['error'][1]) + + form = forms.RegistrationForm(data={'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo'}) + self.assertTrue(form.is_valid()) + + def test_registration_form_username_lowercase(self): + """ + Test that ``RegistrationFormUniqueEmail`` validates uniqueness + of email addresses. + + """ + # Create a user so we can verify that duplicate addresses + # aren't permitted. + UserModel().objects.create_user('alice', 'alice@example.com', 'secret') + + form = forms.RegistrationFormUsernameLowercase(data={'username': 'Alice', + 'email': 'alice@example.com', + 'password1': 'foo', + 'password2': 'foo'}) + self.assertFalse(form.is_valid()) + self.assertEqual(form.errors['username'], + ["A user with that username already exists."]) + + form = forms.RegistrationFormUsernameLowercase(data={'username': 'foo', + 'email': 'alice@example.com', + 'password1': 'foo', + 'password2': 'foo'}) + self.assertTrue(form.is_valid()) + + def test_registration_form_tos(self): + """ + Test that ``RegistrationFormTermsOfService`` requires + agreement to the terms of service. + + """ + form = forms.RegistrationFormTermsOfService(data={'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo'}) + self.assertFalse(form.is_valid()) + self.assertEqual(form.errors['tos'], + ["You must agree to the terms to register"]) + + form = forms.RegistrationFormTermsOfService(data={'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo', + 'tos': 'on'}) + self.assertTrue(form.is_valid()) + + def test_registration_form_unique_email(self): + """ + Test that ``RegistrationFormUniqueEmail`` validates uniqueness + of email addresses. + + """ + # Create a user so we can verify that duplicate addresses + # aren't permitted. + UserModel().objects.create_user('alice', 'alice@example.com', 'secret') + + form = forms.RegistrationFormUniqueEmail(data={'username': 'foo', + 'email': 'alice@example.com', + 'password1': 'foo', + 'password2': 'foo'}) + self.assertFalse(form.is_valid()) + self.assertEqual(form.errors['email'], + ["This email address is already in use. Please supply a different email address."]) + + form = forms.RegistrationFormUniqueEmail(data={'username': 'foo', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo'}) + self.assertTrue(form.is_valid()) + + def test_registration_form_no_free_email(self): + """ + Test that ``RegistrationFormNoFreeEmail`` disallows + registration with free email addresses. + + """ + base_data = {'username': 'foo', + 'password1': 'foo', + 'password2': 'foo'} + for domain in forms.RegistrationFormNoFreeEmail.bad_domains: + invalid_data = base_data.copy() + invalid_data['email'] = "foo@%s" % domain + form = forms.RegistrationFormNoFreeEmail(data=invalid_data) + self.assertFalse(form.is_valid()) + self.assertEqual(form.errors['email'], + ["Registration using free email addresses is prohibited. Please supply a different email address."]) + + base_data['email'] = 'foo@example.com' + form = forms.RegistrationFormNoFreeEmail(data=base_data) + self.assertTrue(form.is_valid()) diff --git a/pycons-site/registration/tests/forms_custom_user.py b/pycons-site/registration/tests/forms_custom_user.py new file mode 100644 index 0000000..d2c78d4 --- /dev/null +++ b/pycons-site/registration/tests/forms_custom_user.py @@ -0,0 +1,45 @@ +from importlib import reload + +from django.test import TestCase +from django.test.utils import override_settings + +from registration import forms +from registration.users import UsernameField + + +@override_settings(AUTH_USER_MODEL='test_app.CustomUser') +class RegistrationFormTests(TestCase): + """ + Test the default registration forms. + + """ + + def setUp(self): + # The form's Meta class is created on import. We have to reload() + # to apply the new AUTH_USER_MODEL to the Meta class. + reload(forms) + + def test_registration_form_adds_custom_user_name_field(self): + """ + Test that ``RegistrationForm`` adds custom username + field and does not raise errors + + """ + + form = forms.RegistrationForm() + + self.assertTrue(UsernameField() in form.fields) + + def test_registration_form_subclass_is_valid(self): + """ + Test that ``RegistrationForm`` subclasses can save + + """ + data = {'new_field': 'custom username', + 'email': 'foo@example.com', + 'password1': 'foo', + 'password2': 'foo'} + + form = forms.RegistrationForm(data=data) + + self.assertTrue(form.is_valid()) diff --git a/pycons-site/registration/tests/models.py b/pycons-site/registration/tests/models.py new file mode 100644 index 0000000..fd3e905 --- /dev/null +++ b/pycons-site/registration/tests/models.py @@ -0,0 +1,1123 @@ +import datetime +import hashlib +import random +import re +import string +import warnings +from copy import copy +from datetime import timedelta + +from django.apps import apps +from django.conf import settings +from django.core import mail +from django.core import management +from django.core.exceptions import ImproperlyConfigured +from django.test import TransactionTestCase +from django.test import override_settings +from django.utils.crypto import get_random_string +from django.utils.timezone import now as datetime_now + +from registration.models import RegistrationProfile +from registration.models import SupervisedRegistrationProfile +from registration.users import UserModel + +Site = apps.get_model('sites', 'Site') + + +@override_settings(ACCOUNT_ACTIVATION_DAYS=7, + REGISTRATION_DEFAULT_FROM_EMAIL='registration@email.com', + REGISTRATION_EMAIL_HTML=True, + DEFAULT_FROM_EMAIL='django@email.com') +class RegistrationModelTests(TransactionTestCase): + """ + Test the model and manager used in the default backend. + + """ + user_info = {'username': 'alice', + 'password': 'swordfish', + 'email': 'alice@example.com'} + + registration_profile = RegistrationProfile + + def setUp(self): + warnings.simplefilter('always', UserWarning) + + def test_profile_creation(self): + """ + Creating a registration profile for a user populates the + profile with the correct user and a SHA256 hash to use as + activation key. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + + self.assertEqual(self.registration_profile.objects.count(), 1) + self.assertEqual(profile.user.id, new_user.id) + self.assertTrue(re.match('^[a-f0-9]{40,64}$', profile.activation_key)) + self.assertEqual(str(profile), + "Registration information for alice") + + def test_activation_email(self): + """ + ``RegistrationProfile.send_activation_email`` sends an + email. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.send_activation_email(Site.objects.get_current()) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].to, [self.user_info['email']]) + + @override_settings(ACTIVATION_EMAIL_HTML='does-not-exist') + def test_activation_email_missing_template(self): + """ + ``RegistrationProfile.send_activation_email`` sends an + email. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.send_activation_email(Site.objects.get_current()) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].to, [self.user_info['email']]) + + def test_activation_email_uses_registration_default_from_email(self): + """ + ``RegistrationProfile.send_activation_email`` sends an + email. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.send_activation_email(Site.objects.get_current()) + self.assertEqual(mail.outbox[0].from_email, 'registration@email.com') + + @override_settings(REGISTRATION_DEFAULT_FROM_EMAIL=None) + def test_activation_email_falls_back_to_django_default_from_email(self): + """ + ``RegistrationProfile.send_activation_email`` sends an + email. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.send_activation_email(Site.objects.get_current()) + self.assertEqual(mail.outbox[0].from_email, 'django@email.com') + + @override_settings(REGISTRATION_USE_SITE_EMAIL=True, + REGISTRATION_SITE_USER_EMAIL='admin') + def test_activation_email_uses_site_address(self): + """ + ``RegistrationProfile.send_activation_email`` sends an + email with the ``from`` address configured by the site. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + site = Site.objects.get_current() + profile.send_activation_email(site) + from_email = 'admin@{}'.format(site.domain) + self.assertEqual(mail.outbox[0].from_email, from_email) + + @override_settings(REGISTRATION_USE_SITE_EMAIL=True) + def test_activation_email_uses_site_address_improperly_configured(self): + """ + ``RegistrationProfile.send_activation_email`` won't send an email if + improperly configured. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + with self.assertRaises(ImproperlyConfigured): + profile.send_activation_email(Site.objects.get_current()) + + def test_activation_email_is_html_by_default(self): + """ + ``RegistrationProfile.send_activation_email`` sends an html + email by default. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.send_activation_email(Site.objects.get_current()) + + self.assertEqual(len(mail.outbox[0].alternatives), 1) + + @override_settings(REGISTRATION_EMAIL_HTML=False) + def test_activation_email_is_plain_text_if_html_disabled(self): + """ + ``RegistrationProfile.send_activation_email`` sends a plain + text email if settings.REGISTRATION_EMAIL_HTML is False. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.send_activation_email(Site.objects.get_current()) + + self.assertEqual(len(mail.outbox[0].alternatives), 0) + + def test_user_creation(self): + """ + Creating a new user populates the correct data, and sets the + user's account inactive. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + self.assertEqual(new_user.get_username(), 'alice') + self.assertEqual(new_user.email, 'alice@example.com') + self.assertTrue(new_user.check_password('swordfish')) + self.assertFalse(new_user.is_active) + + expiration_date = datetime_now() - timedelta( + settings.ACCOUNT_ACTIVATION_DAYS + ) + self.assertGreater(new_user.date_joined, expiration_date) + + def test_user_creation_email(self): + """ + By default, creating a new user sends an activation email. + + """ + self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + self.assertEqual(len(mail.outbox), 1) + + def test_user_creation_no_email(self): + """ + Passing ``send_email=False`` when creating a new user will not + send an activation email. + + """ + self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), + send_email=False, **self.user_info) + self.assertEqual(len(mail.outbox), 0) + + def test_user_creation_old_date_joined(self): + """ + If ``user.date_joined`` is well in the past, ensure that we reset it. + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + self.assertEqual(new_user.get_username(), 'alice') + self.assertEqual(new_user.email, 'alice@example.com') + self.assertTrue(new_user.check_password('swordfish')) + self.assertFalse(new_user.is_active) + + expiry_date = datetime_now() - timedelta(settings.ACCOUNT_ACTIVATION_DAYS) + self.assertGreater(new_user.date_joined, expiry_date) + + def test_unexpired_account_old_date_joined(self): + """ + ``RegistrationProfile.activation_key_expired()`` is ``False`` within + the activation window. Even if the user was created in the past. + + """ + self.user_info['date_joined'] = datetime_now( + ) - timedelta(settings.ACCOUNT_ACTIVATION_DAYS + 1) + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + self.assertFalse(profile.activation_key_expired()) + + def test_unexpired_account(self): + """ + ``RegistrationProfile.activation_key_expired()`` is ``False`` + within the activation window. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + self.assertFalse(profile.activation_key_expired()) + + def test_active_account_activation_key_expired(self): + """ + ``RegistrationProfile.activation_key_expired()`` is ``True`` + when the account is already active. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + profile.refresh_from_db() + self.assertTrue(profile.activation_key_expired()) + + def test_active_account_and_expired_accountactivation_key_expired(self): + """ + ``RegistrationProfile.activation_key_expired()`` is ``True`` + when the account is already active and the activation window has passed. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + new_user.date_joined -= datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + new_user.save() + profile = self.registration_profile.objects.get(user=new_user) + self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + profile.refresh_from_db() + self.assertTrue(profile.activation_key_expired()) + + def test_expired_account(self): + """ + ``RegistrationProfile.activation_key_expired()`` is ``True`` + outside the activation window. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + new_user.date_joined -= datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + new_user.save() + profile = self.registration_profile.objects.get(user=new_user) + self.assertTrue(profile.activation_key_expired()) + + def test_valid_activation(self): + """ + Activating a user within the permitted window makes the + account active, and resets the activation key. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + self.assertIsInstance(user, UserModel()) + self.assertEqual(user.id, new_user.id) + self.assertTrue(user.is_active) + self.assertTrue(activated) + + profile = self.registration_profile.objects.get(user=new_user) + self.assertTrue(profile.activated) + + def test_valid_activation_with_profile(self): + """ + Activating a user within the permitted window makes the + account active, and resets the activation key. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + profile, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current(), get_profile=True) + + self.assertIsInstance(profile, self.registration_profile) + self.assertEqual(profile.id, profile.id) + self.assertTrue(profile.activated) + self.assertTrue(activated) + + new_user.refresh_from_db() + self.assertTrue(profile.user.id, new_user.id) + self.assertTrue(new_user.is_active) + + def test_expired_activation(self): + """ + Attempting to activate outside the permitted window does not + activate the account. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + new_user.date_joined -= datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + new_user.save() + + profile = self.registration_profile.objects.get(user=new_user) + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + self.assertIs(user, False) + self.assertFalse(activated) + + new_user = UserModel().objects.get(username='alice') + self.assertFalse(new_user.is_active) + + profile = self.registration_profile.objects.get(user=new_user) + self.assertFalse(profile.activated) + + def test_activation_invalid_key(self): + """ + Attempting to activate with a key which is not a SHA256 hash + fails. + + """ + user, activated = self.registration_profile.objects.activate_user( + 'foo', Site.objects.get_current()) + self.assertIs(user, False) + self.assertFalse(activated) + + def test_activation_already_activated(self): + """ + Attempting to re-activate an already-activated account fails. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + profile = self.registration_profile.objects.get(user=new_user) + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + self.assertEqual(user, new_user) + self.assertFalse(activated) + + def test_activation_deactivated(self): + """ + Attempting to re-activate a deactivated account fails. + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + # Deactivate the new user. + new_user.is_active = False + new_user.save() + + # Try to activate again and ensure False is returned. + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + self.assertFalse(activated) + + def test_activation_nonexistent_key(self): + """ + Attempting to activate with a non-existent key (i.e., one not + associated with any account) fails. + + """ + # Due to the way activation keys are constructed during + # registration, this will never be a valid key. + invalid_key = hashlib.sha256('foo'.encode('latin-1')).hexdigest() + _, activated = self.registration_profile.objects.activate_user( + invalid_key, Site.objects.get_current()) + self.assertFalse(activated) + + def test_expired_user_deletion_activation_window(self): + """ + ``RegistrationProfile.objects.delete_expired_users()`` only + deletes inactive users whose activation window has expired. + + """ + self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + expired_user = (self.registration_profile.objects + .create_inactive_user( + site=Site.objects.get_current(), + username='bob', + password='secret', + email='bob@example.com')) + expired_user.date_joined -= datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + expired_user.save() + + deleted_count = self.registration_profile.objects.delete_expired_users() + self.assertEqual(deleted_count, 1) + self.assertEqual(self.registration_profile.objects.count(), 1) + self.assertRaises(UserModel().DoesNotExist, + UserModel().objects.get, username='bob') + + def test_expired_user_deletion_ignore_activated(self): + """ + ``RegistrationProfile.objects.delete_expired_users()`` only + deletes inactive users whose activation window has expired and if + their profile is not activated. + + """ + user = (self.registration_profile.objects + .create_inactive_user( + site=Site.objects.get_current(), + username='bob', + password='secret', + email='bob@example.com')) + profile = self.registration_profile.objects.get(user=user) + _, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + self.assertTrue(activated) + # Expire the activation window. + user.date_joined -= datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + user.save() + + deleted_count = self.registration_profile.objects.delete_expired_users() + self.assertEqual(deleted_count, 0) + self.assertEqual(self.registration_profile.objects.count(), 1) + self.assertEqual(UserModel().objects.get(username='bob'), user) + + def test_expired_user_deletion_missing_user(self): + """ + ``RegistrationProfile.objects.delete_expired_users()`` only deletes + inactive users whose activation window has expired. If a ``UserModel`` + is not present, the delete continues gracefully. + + """ + self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + expired_user = (self.registration_profile.objects + .create_inactive_user( + site=Site.objects.get_current(), + username='bob', + password='secret', + email='bob@example.com')) + expired_user.date_joined -= datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + expired_user.save() + # Ensure that we cleanup the expired profile even if the user does not + # exist. We simulate this with raw SQL, calling `expired_user.delete()` + # would result in the profile being deleted on cascade. + UserModel().objects.raw('DELETE FROM users WHERE username="bob"') + + deleted_count = self.registration_profile.objects.delete_expired_users() + self.assertEqual(deleted_count, 1) + self.assertEqual(self.registration_profile.objects.count(), 1) + self.assertRaises(UserModel().DoesNotExist, + UserModel().objects.get, username='bob') + + def test_manually_registered_account(self): + """ + Test if a user failed to go through the registration flow but was + manually marked ``is_active`` in the DB. Although the profile is + expired and not active, we should never delete active users. + """ + active_user = (self.registration_profile.objects + .create_inactive_user( + site=Site.objects.get_current(), + username='bob', + password='secret', + email='bob@example.com')) + active_user.date_joined -= datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + active_user.is_active = True + active_user.save() + + deleted_count = self.registration_profile.objects.delete_expired_users() + self.assertEqual(deleted_count, 0) + self.assertEqual(self.registration_profile.objects.count(), 1) + self.assertEqual(UserModel().objects.get(username='bob'), active_user) + + def test_management_command(self): + """ + The ``cleanupregistration`` management command properly + deletes expired accounts. + + """ + self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + expired_user = (self.registration_profile.objects + .create_inactive_user(site=Site.objects.get_current(), + username='bob', + password='secret', + email='bob@example.com')) + expired_user.date_joined -= datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + expired_user.save() + + management.call_command('cleanupregistration') + self.assertEqual(self.registration_profile.objects.count(), 1) + self.assertRaises(UserModel().DoesNotExist, + UserModel().objects.get, username='bob') + + def test_resend_activation_email(self): + """ + Test resending activation email to an existing user + """ + user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), send_email=False, **self.user_info) + self.assertEqual(len(mail.outbox), 0) + + profile = self.registration_profile.objects.get(user=user) + orig_activation_key = profile.activation_key + + self.assertTrue(self.registration_profile.objects.resend_activation_mail( + email=self.user_info['email'], + site=Site.objects.get_current(), + )) + + profile = self.registration_profile.objects.get(pk=profile.pk) + new_activation_key = profile.activation_key + + self.assertNotEqual(orig_activation_key, new_activation_key) + self.assertEqual(len(mail.outbox), 1) + + def test_resend_activation_email_nonexistent_user(self): + """ + Test resending activation email to a nonexisting user + """ + self.assertFalse(self.registration_profile.objects.resend_activation_mail( + email=self.user_info['email'], + site=Site.objects.get_current(), + )) + self.assertEqual(len(mail.outbox), 0) + + def test_resend_activation_email_activated_user(self): + """ + Test the scenario where user tries to resend activation code + to the already activated user's email + """ + user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), send_email=False, **self.user_info) + + profile = self.registration_profile.objects.get(user=user) + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + self.assertTrue(user.is_active) + self.assertTrue(activated) + + self.assertFalse(self.registration_profile.objects.resend_activation_mail( + email=self.user_info['email'], + site=Site.objects.get_current(), + )) + self.assertEqual(len(mail.outbox), 0) + + def test_resend_activation_email_expired_user(self): + """ + Test the scenario where user tries to resend activation code + to the expired user's email + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), send_email=False, **self.user_info) + new_user.date_joined -= datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + new_user.save() + + profile = self.registration_profile.objects.get(user=new_user) + self.assertTrue(profile.activation_key_expired()) + + self.assertFalse(self.registration_profile.objects.resend_activation_mail( + email=self.user_info['email'], + site=Site.objects.get_current(), + )) + self.assertEqual(len(mail.outbox), 0) + + def test_resend_activation_email_nonunique_email(self): + """ + Test the scenario where user tries to resend activation code + to the expired user's email + """ + user1 = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), send_email=False, **self.user_info) + user2_info = copy(self.user_info) + user2_info['username'] = 'bob' + user2 = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), send_email=False, **user2_info) + self.assertEqual(user1.email, user2.email) + self.assertFalse(self.registration_profile.objects.resend_activation_mail( + email=self.user_info['email'], + site=Site.objects.get_current(), + )) + self.assertEqual(len(mail.outbox), 0) + + def test_activation_key_backwards_compatibility(self): + """ + Make sure that users created with the old create_new_activation_key + method can still be activated. + """ + current_method = self.registration_profile.create_new_activation_key + + def old_method(self, save=True): + salt = hashlib.sha1( + str(random.random()).encode('ascii') + ).hexdigest()[:5] + salt = salt.encode('ascii') + user_pk = str(self.user.pk) + if isinstance(user_pk, str): + user_pk = user_pk.encode() + self.activation_key = hashlib.sha1(salt + user_pk).hexdigest() + if save: + self.save() + return self.activation_key + + self.registration_profile.create_new_activation_key = old_method + + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + + self.registration_profile.create_new_activation_key = current_method + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + self.assertIsInstance(user, UserModel()) + self.assertEqual(user.id, new_user.id) + self.assertTrue(user.is_active) + self.assertTrue(activated) + + profile = self.registration_profile.objects.get(user=new_user) + self.assertTrue(profile.activated) + + def test_activation_key_backwards_compatibility_sha1(self): + """ + Make sure that users created with the old create_new_activation_key + method can still be activated. + """ + current_method = self.registration_profile.create_new_activation_key + + def old_method(self, save=True): + random_string = get_random_string(length=32, allowed_chars=string.printable) + self.activation_key = hashlib.sha1(random_string.encode()).hexdigest() + if save: + self.save() + return self.activation_key + + self.registration_profile.create_new_activation_key = old_method + + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + + self.registration_profile.create_new_activation_key = current_method + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + self.assertIsInstance(user, UserModel()) + self.assertEqual(user.id, new_user.id) + self.assertTrue(user.is_active) + self.assertTrue(activated) + + profile = self.registration_profile.objects.get(user=new_user) + self.assertTrue(profile.activated) + + +@override_settings( + ADMINS=( + ('T-Rex', 'admin1@iamtrex.com'), + ('Flea', 'admin2@iamaflea.com') + ) +) +class SupervisedRegistrationModelTests(RegistrationModelTests): + """ + Test the model and manager used in the admin_approval backend. + + """ + + user_info = {'username': 'alice', + 'password': 'swordfish', + 'email': 'alice@example.com'} + + registration_profile = SupervisedRegistrationProfile + + def test_valid_activation(self): + """ + Activating a user within the permitted window makes the + account active, and resets the activation key. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + self.assertIsInstance(user, UserModel()) + self.assertEqual(user.id, new_user.id) + self.assertFalse(user.is_active) + self.assertTrue(activated) + + profile = self.registration_profile.objects.get(user=new_user) + self.assertTrue(profile.activated) + + def test_valid_activation_with_profile(self): + """ + Activating a user within the permitted window makes the + account active, and resets the activation key. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + profile, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current(), get_profile=True) + + self.assertIsInstance(profile, self.registration_profile) + self.assertEqual(profile.id, profile.id) + self.assertTrue(profile.activated) + self.assertTrue(activated) + + new_user.refresh_from_db() + self.assertTrue(profile.user.id, new_user.id) + self.assertFalse(new_user.is_active) + + def test_resend_activation_email_activated_user(self): + """ + Test the scenario where user tries to resend activation code + to the already activated user's email + """ + user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), send_email=False, **self.user_info) + + profile = self.registration_profile.objects.get(user=user) + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + self.assertFalse(user.is_active) + self.assertTrue(activated) + + self.assertFalse(self.registration_profile.objects.resend_activation_mail( + email=self.user_info['email'], + site=Site.objects.get_current(), + )) + # Outbox has one mail, admin approve mail + + self.assertEqual(len(mail.outbox), 1) + admins_emails = [value[1] for value in settings.REGISTRATION_ADMINS] + for email in mail.outbox[0].to: + self.assertIn(email, admins_emails) + + def test_admin_approval_email(self): + """ + ``SupervisedRegistrationManager.send_admin_approve_email`` sends an + email to the site administrators + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.activated = True + self.registration_profile.objects.send_admin_approve_email( + new_user, Site.objects.get_current()) + self.assertEqual(len(mail.outbox), 1) + admins_emails = [value[1] for value in settings.REGISTRATION_ADMINS] + for email in mail.outbox[0].to: + self.assertIn(email, admins_emails) + + def test_admin_approval_email_uses_registration_default_from_email(self): + """ + ``SupervisedRegistrationManager.send_admin_approve_email``` sends an + email. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.activated = True + self.registration_profile.objects.send_admin_approve_email( + new_user, Site.objects.get_current()) + self.assertEqual(mail.outbox[0].from_email, 'registration@email.com') + + @override_settings(REGISTRATION_DEFAULT_FROM_EMAIL=None) + def test_admin_approval_email_falls_back_to_django_default_from_email(self): + """ + ``SupervisedRegistrationManager.send_admin_approve_email`` sends an + email. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.activated = True + self.registration_profile.objects.send_admin_approve_email( + new_user, Site.objects.get_current()) + self.assertEqual(mail.outbox[0].from_email, 'django@email.com') + + def test_admin_approval_email_is_html_by_default(self): + """ + ``SupervisedRegistrationProfile.send_activation_email`` sends an html + email by default. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.activated = True + self.registration_profile.objects.send_admin_approve_email( + new_user, Site.objects.get_current()) + + self.assertEqual(len(mail.outbox[0].alternatives), 1) + + @override_settings(REGISTRATION_EMAIL_HTML=False) + def test_admin_approval_email_is_plain_text_if_html_disabled(self): + """ + ``SupervisedRegistrationProfile.send_activation_email`` sends a plain + text email if settings.REGISTRATION_EMAIL_HTML is False. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.activated = True + self.registration_profile.objects.send_admin_approve_email( + new_user, Site.objects.get_current()) + + self.assertEqual(len(mail.outbox[0].alternatives), 0) + + def test_active_account_activation_key_expired(self): + """ + ``SupervisedRegistrationProfile.activation_key_expired()`` is ``True`` + when the account is already active. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + self.registration_profile.objects.admin_approve_user( + profile.id, Site.objects.get_current()) + profile.refresh_from_db() + self.assertTrue(profile.activation_key_expired()) + + def test_active_account_and_expired_accountactivation_key_expired(self): + """ + ``SupervisedRegistrationProfile.activation_key_expired()`` is ``True`` + when the account is already active and the activation window has passed. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + new_user.date_joined -= datetime.timedelta( + days=settings.ACCOUNT_ACTIVATION_DAYS + 1) + new_user.save() + profile = self.registration_profile.objects.get(user=new_user) + self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + self.registration_profile.objects.admin_approve_user( + profile.id, Site.objects.get_current()) + profile.refresh_from_db() + self.assertTrue(profile.activation_key_expired()) + + def test_admin_approval_complete_email(self): + """ + ``SupervisedRegistrationManager.send_admin_approve_complete_email`` + sends an email to the approved user + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.send_admin_approve_complete_email(Site.objects.get_current()) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].to, [self.user_info['email']]) + + def test_admin_approval_complete_email_uses_registration_default_from_email(self): + """ + ``SupervisedRegistrationManager.send_admin_approve_complete_email`` + sends an email + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.send_admin_approve_complete_email(Site.objects.get_current()) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].from_email, 'registration@email.com') + + @override_settings(REGISTRATION_DEFAULT_FROM_EMAIL=None) + def test_admin_approval_complete_email_falls_back_to_django_default_from_email(self): + """ + ``SupervisedRegistrationManager.send_admin_approve_complete_email`` + sends an email + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.send_admin_approve_complete_email(Site.objects.get_current()) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(mail.outbox[0].from_email, 'django@email.com') + + def test_admin_approval_complete_email_is_html_by_default(self): + """ + ``SupervisedRegistrationProfile.send_admin_approve_complete_email`` + sends an html email by default. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.send_admin_approve_complete_email(Site.objects.get_current()) + self.assertEqual(len(mail.outbox), 1) + self.assertEqual(len(mail.outbox[0].alternatives), 1) + + @override_settings(REGISTRATION_EMAIL_HTML=False) + def test_admin_approval_complete_email_is_plain_text_if_html_disabled(self): + """ + ``SupervisedRegistrationProfile.send_admin_approve_complete_email`` + sends a plain text email if settings.REGISTRATION_EMAIL_HTML is False. + + """ + new_user = UserModel().objects.create_user(**self.user_info) + profile = self.registration_profile.objects.create_profile(new_user) + profile.send_admin_approve_complete_email(Site.objects.get_current()) + self.assertEqual(len(mail.outbox), 1) + + self.assertEqual(len(mail.outbox[0].alternatives), 0) + + def test_valid_admin_approval(self): + """ + Approving an already activated user's account makes the user + active + """ + + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + self.assertIsInstance(user, UserModel()) + + user = self.registration_profile.objects.admin_approve_user( + profile.id, Site.objects.get_current()) + self.assertIsInstance(user, UserModel()) + self.assertIs(user.is_active, True) + + def test_admin_approval_not_activated(self): + """ + Approving a non activated user's account fails + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + + user = self.registration_profile.objects.admin_approve_user( + profile.id, Site.objects.get_current()) + self.assertIs(user, False) + self.assertIs(profile.user.is_active, False) + + def test_admin_approval_already_approved(self): + """ + Approving an already approved user's account returns the User model + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + self.assertIsInstance(user, UserModel()) + self.assertTrue(activated) + + user = self.registration_profile.objects.admin_approve_user( + profile.id, Site.objects.get_current()) + self.assertIsInstance(user, UserModel()) + self.assertIs(user.is_active, True) + + def test_admin_approval_nonexistent_id(self): + """ + Approving a non existent user profile does nothing and returns False + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + + user = self.registration_profile.objects.admin_approve_user( + profile.id, Site.objects.get_current()) + self.assertIs(user, False) + + def test_activation_already_activated(self): + """ + Attempting to re-activate an already-activated account fails. + + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + profile = self.registration_profile.objects.get(user=new_user) + _, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + self.assertFalse(activated) + + def test_activation_key_backwards_compatibility(self): + """ + Make sure that users created with the old create_new_activation_key + method can still be activated. + """ + current_method = self.registration_profile.create_new_activation_key + + def old_method(self, save=True): + salt = hashlib.sha1( + str(random.random()).encode('ascii') + ).hexdigest()[:5] + salt = salt.encode('ascii') + user_pk = str(self.user.pk) + if isinstance(user_pk, str): + user_pk = user_pk.encode() + self.activation_key = hashlib.sha1(salt + user_pk).hexdigest() + if save: + self.save() + return self.activation_key + + self.registration_profile.create_new_activation_key = old_method + + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + + self.registration_profile.create_new_activation_key = current_method + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + self.assertIsInstance(user, UserModel()) + self.assertEqual(user.id, new_user.id) + self.assertFalse(user.is_active) + self.assertTrue(activated) + + profile = self.registration_profile.objects.get(user=new_user) + self.assertTrue(profile.activated) + + def test_activation_key_backwards_compatibility_sha1(self): + """ + Make sure that users created with the old create_new_activation_key + method can still be activated. + """ + current_method = self.registration_profile.create_new_activation_key + + def old_method(self, save=True): + random_string = get_random_string(length=32, allowed_chars=string.printable) + self.activation_key = hashlib.sha1(random_string.encode()).hexdigest() + if save: + self.save() + return self.activation_key + + self.registration_profile.create_new_activation_key = old_method + + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + profile = self.registration_profile.objects.get(user=new_user) + + self.registration_profile.create_new_activation_key = current_method + user, activated = self.registration_profile.objects.activate_user( + profile.activation_key, Site.objects.get_current()) + + self.assertIsInstance(user, UserModel()) + self.assertEqual(user.id, new_user.id) + self.assertFalse(user.is_active) + self.assertTrue(activated) + + profile = self.registration_profile.objects.get(user=new_user) + self.assertTrue(profile.activated) + + @override_settings(ADMINS=(), REGISTRATION_ADMINS=()) + def test_no_admins_registered(self): + """ + Approving a non existent user profile does nothing and returns False + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + + with self.assertRaises(ImproperlyConfigured): + self.registration_profile.objects.send_admin_approve_email( + new_user, Site.objects.get_current()) + + @override_settings(REGISTRATION_ADMINS=()) + def test_no_registration_admins_registered(self): + """ + Approving a non existent user profile does nothing and returns False + """ + new_user = self.registration_profile.objects.create_inactive_user( + site=Site.objects.get_current(), **self.user_info) + + with warnings.catch_warnings(record=True) as _warning: + self.registration_profile.objects.send_admin_approve_email( + new_user, Site.objects.get_current()) + + assertion_error = '''No warning triggered for unregistered + REGISTRATION_ADMINS''' + self.assertTrue(len(_warning) > 0, assertion_error) + self.assertTrue('REGISTRATION_ADMINS' in str(_warning[-1].message), + assertion_error) diff --git a/pycons-site/registration/tests/simple_backend.py b/pycons-site/registration/tests/simple_backend.py new file mode 100644 index 0000000..6ea8138 --- /dev/null +++ b/pycons-site/registration/tests/simple_backend.py @@ -0,0 +1,86 @@ +from django.conf import settings +from django.test import TestCase +from django.test import override_settings +from django.urls import reverse + +from registration.forms import RegistrationForm +from registration.users import UserModel + + +@override_settings(ROOT_URLCONF='test_app.urls_simple') +class SimpleBackendViewTests(TestCase): + + @override_settings(REGISTRATION_OPEN=True) + def test_registration_open(self): + """ + The setting ``REGISTRATION_OPEN`` appropriately controls + whether registration is permitted. + + """ + resp = self.client.get(reverse('registration_register')) + self.assertEqual(200, resp.status_code) + + @override_settings(REGISTRATION_OPEN=False) + def test_registration_closed(self): + + # Now all attempts to hit the register view should redirect to + # the 'registration is closed' message. + resp = self.client.get(reverse('registration_register')) + self.assertRedirects(resp, reverse('registration_disallowed')) + + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + self.assertRedirects(resp, reverse('registration_disallowed')) + + def test_registration_get(self): + """ + HTTP ``GET`` to the registration view uses the appropriate + template and populates a registration form into the context. + + """ + resp = self.client.get(reverse('registration_register')) + self.assertEqual(200, resp.status_code) + self.assertTemplateUsed(resp, + 'registration/registration_form.html') + self.assertIsInstance(resp.context['form'], RegistrationForm) + + def test_registration(self): + """ + Registration creates a new account and logs the user in. + + """ + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'secret'}) + new_user = UserModel().objects.get(username='bob') + self.assertEqual(302, resp.status_code) + self.assertIn(getattr(settings, 'SIMPLE_BACKEND_REDIRECT_URL', '/'), + resp['Location']) + + self.assertTrue(new_user.check_password('secret')) + self.assertEqual(new_user.email, 'bob@example.com') + + # New user must be active. + self.assertTrue(new_user.is_active) + + # New user must be logged in. + resp = self.client.get(reverse('registration_register'), follow=True) + self.assertTrue(resp.context['user'].is_authenticated) + + def test_registration_failure(self): + """ + Registering with invalid data fails. + + """ + resp = self.client.post(reverse('registration_register'), + data={'username': 'bob', + 'email': 'bob@example.com', + 'password1': 'secret', + 'password2': 'notsecret'}) + self.assertEqual(200, resp.status_code) + self.assertFalse(resp.context['form'].is_valid()) diff --git a/pycons-site/registration/tests/urls.py b/pycons-site/registration/tests/urls.py new file mode 100644 index 0000000..55ddb50 --- /dev/null +++ b/pycons-site/registration/tests/urls.py @@ -0,0 +1,79 @@ +""" +URLs used in the unit tests for django-registration. + +You should not attempt to use these URLs in any sort of real or +development environment; instead, use +``registration/backends/default/urls.py``. This URLconf includes those +URLs, and also adds several additional URLs which serve no purpose +other than to test that optional keyword arguments are properly +handled. + +""" + +from django.conf.urls import include +from django.urls import path +from django.views.generic import TemplateView + +from registration.views import ActivationView +from registration.views import RegistrationView + +urlpatterns = [ + # Test the 'activate' view with custom template + # name. + path('activate-with-template-name//', + ActivationView.as_view(), + {'template_name': 'registration/test_template_name.html', + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_activate_template_name'), + # Test the 'activate' view with + # extra_context_argument. + path('activate-extra-context//', + ActivationView.as_view(), + {'extra_context': {'foo': 'bar', 'callable': lambda: 'called'}, + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_activate_extra_context'), + # Test the 'activate' view with success_url argument. + path('activate-with-success-url//', + ActivationView.as_view(), + {'success_url': 'registration_test_custom_success_url', + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_activate_success_url'), + # Test the 'register' view with custom template + # name. + path('register-with-template-name/', + RegistrationView.as_view(), + {'template_name': 'registration/test_template_name.html', + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_register_template_name'), + # Test the'register' view with extra_context + # argument. + path('register-extra-context/', + RegistrationView.as_view(), + {'extra_context': {'foo': 'bar', 'callable': lambda: 'called'}, + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_register_extra_context'), + # Test the 'register' view with custom URL for + # closed registration. + path('register-with-disallowed-url/', + RegistrationView.as_view(), + {'disallowed_url': 'registration_test_custom_disallowed', + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_register_disallowed_url'), + # Set up a pattern which will correspond to the + # custom 'disallowed_url' above. + path('custom-disallowed/', + TemplateView.as_view(template_name='registration/registration_closed.html'), + name='registration_test_custom_disallowed'), + # Test the 'register' view with custom redirect + # on successful registration. + path('register-with-success_url/', + RegistrationView.as_view(), + {'success_url': 'registration_test_custom_success_url', + 'backend': 'registration.backends.default.DefaultBackend'}, + name='registration_test_register_success_url'), + # Pattern for custom redirect set above. + path('custom-success/', + TemplateView.as_view(template_name='registration/test_template_name.html'), + name='registration_test_custom_success_url'), + path('', include('registration.backends.default.urls')), +] diff --git a/pycons-site/registration/urls.py b/pycons-site/registration/urls.py new file mode 100644 index 0000000..1eb3d5b --- /dev/null +++ b/pycons-site/registration/urls.py @@ -0,0 +1,20 @@ +from django.urls import path, re_path +from django.conf.urls.static import static +from django.contrib.auth.decorators import login_required + +from .views import * + +app_name = 'registration' + +urlpatterns = [ + path('create_profile/', login_required(CreateProfileView.as_view()), name='create_profile'), + path('update//', UpdateProfileView.as_view(), name='profile_update'), + path('', login_required(ProfileView.as_view()), name='profile_home'), + re_path('password_change/(?P\d+)/', login_required(PasswordView.as_view()), name='password_change'), + re_path('login_details/(?P\d+)/', login_required(UpdateLoginView.as_view()), name='login_details'), + path('profile_updated/', login_required(SuccessView.as_view()), name='profile_update'), +] + +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/pycons-site/registration/users.py b/pycons-site/registration/users.py new file mode 100644 index 0000000..4246fec --- /dev/null +++ b/pycons-site/registration/users.py @@ -0,0 +1,15 @@ +from django.conf import settings +from django.contrib.auth import get_user_model + +UserModel = get_user_model + + +def UserModelString(): + try: + return settings.AUTH_USER_MODEL + except AttributeError: + return 'auth.User' + + +def UsernameField(): + return getattr(UserModel(), 'USERNAME_FIELD', 'username') diff --git a/pycons-site/registration/utils.py b/pycons-site/registration/utils.py new file mode 100644 index 0000000..42eee0f --- /dev/null +++ b/pycons-site/registration/utils.py @@ -0,0 +1,6 @@ +from django import VERSION as DJANGO_VERSION + +if DJANGO_VERSION[0] < 3: + from django.utils.translation import gettext_lazy as _ # noqa +else: + from django.utils.translation import gettext_lazy as _ # noqa diff --git a/pycons-site/registration/views.py b/pycons-site/registration/views.py new file mode 100644 index 0000000..e5a7633 --- /dev/null +++ b/pycons-site/registration/views.py @@ -0,0 +1,318 @@ +""" +Views which allow users to create and activate accounts. + +""" + +from django.conf import settings +from django.utils.decorators import method_decorator +from django.utils.module_loading import import_string +from django.views.decorators.debug import sensitive_post_parameters +from django.views.generic.edit import FormView + +from registration.forms import ResendActivationForm + +# Included by me (3rd Parties and more) +from django.contrib.auth.models import User +from datetime import datetime +from .forms import UpdateForm, UserForm, PasswordForm +from .models import Profile +from .mixins import EditOwnLoginMixin + +from django.shortcuts import get_object_or_404, render, redirect +from django.views.generic.edit import UpdateView +from django.views.generic.base import TemplateView +from django.urls import reverse_lazy +from django.http import HttpResponseRedirect + +REGISTRATION_FORM_PATH = getattr(settings, 'REGISTRATION_FORM', + 'registration.forms.RegistrationForm') +REGISTRATION_FORM = import_string(REGISTRATION_FORM_PATH) +ACCOUNT_AUTHENTICATED_REGISTRATION_REDIRECTS = getattr( + settings, 'ACCOUNT_AUTHENTICATED_REGISTRATION_REDIRECTS', True) + + +class RegistrationView(FormView): + """ + Base class for user registration views. + + """ + disallowed_url = 'registration_disallowed' + form_class = REGISTRATION_FORM + http_method_names = ['get', 'post', 'head', 'options', 'trace'] + success_url = None + template_name = 'registration/registration_form.html' + + @method_decorator(sensitive_post_parameters('password1', 'password2')) + def dispatch(self, request, *args, **kwargs): + """ + Check that user signup is allowed and if user is logged in before even bothering to + dispatch or do other processing. + + """ + if ACCOUNT_AUTHENTICATED_REGISTRATION_REDIRECTS: + if self.request.user.is_authenticated: + if settings.LOGIN_REDIRECT_URL is not None: + return redirect(settings.LOGIN_REDIRECT_URL) + else: + raise Exception(( + 'You must set a URL with LOGIN_REDIRECT_URL in ' + 'settings.py or set ' + 'ACCOUNT_AUTHENTICATED_REGISTRATION_REDIRECTS=False')) + + if not self.registration_allowed(): + return redirect(self.disallowed_url) + return super().dispatch(request, *args, **kwargs) + + def form_valid(self, form): + new_user = self.register(form) + success_url = self.get_success_url(new_user) + + # success_url may be a simple string, or a tuple providing the + # full argument set for redirect(). Attempting to unpack it + # tells us which one it is. + try: + to, args, kwargs = success_url + except ValueError: + return redirect(success_url) + else: + return redirect(to, *args, **kwargs) + + def registration_allowed(self): + """ + Override this to enable/disable user registration, either + globally or on a per-request basis. + + """ + return True + + def register(self, form): + """ + Implement user-registration logic here. + + """ + raise NotImplementedError + + def get_success_url(self, user=None): + """ + Use the new user when constructing success_url. + + """ + return super().get_success_url() + + +class ActivationView(TemplateView): + """ + Base class for user activation views. + + """ + http_method_names = ['get'] + template_name = 'registration/activate.html' + + def get(self, request, *args, **kwargs): + activated_user = self.activate(*args, **kwargs) + if activated_user: + success_url = self.get_success_url(activated_user) + try: + to, args, kwargs = success_url + except ValueError: + return redirect(success_url) + else: + return redirect(to, *args, **kwargs) + return super().get(request, *args, **kwargs) + + def activate(self, *args, **kwargs): + """ + Implement account-activation logic here. + + """ + raise NotImplementedError + + def get_success_url(self, user): + raise NotImplementedError + + +class ResendActivationView(FormView): + """ + Base class for resending activation views. + """ + form_class = ResendActivationForm + template_name = 'registration/resend_activation_form.html' + + def form_valid(self, form): + """ + Regardless if resend_activation is successful, display the same + confirmation template. + + """ + self.resend_activation(form) + return self.render_form_submitted_template(form) + + def resend_activation(self, form): + """ + Implement resend activation key logic here. + """ + raise NotImplementedError + + def render_form_submitted_template(self, form): + """ + Implement rendering of confirmation template here. + + """ + raise NotImplementedError + + +class ApprovalView(TemplateView): + + http_method_names = ['get'] + template_name = 'registration/admin_approve.html' + + def get(self, request, *args, **kwargs): + approved_user = self.approve(*args, **kwargs) + if approved_user: + success_url = self.get_success_url(approved_user) + try: + to, args, kwargs = success_url + except ValueError: + return redirect(success_url) + else: + return redirect(to, *args, **kwargs) + return super().get(request, *args, **kwargs) + + def approve(self, *args, **kwargs): + """ + Implement admin-approval logic here. + + """ + raise NotImplementedError + + def get_success_url(self, user): + raise NotImplementedError + + + +class ProfileView(TemplateView): + template_name = "profiles/home.html" + + def get_context_data(self, **kwargs): + context = super(ProfileView, self).get_context_data(**kwargs) + context['title'] = "My Profile" + context['year'] = datetime.now().year + context['details'] = User.objects.filter(username=self.request.user) + try: + context['user_profile'] = Profile.objects.filter(user_id=self.request.user.pk) + except Profile.DoesNotExist: + context['user_profile'] = '' + return context + +class CreateProfileView(TemplateView): + template_name = "profiles/create_profile.html" + + def get_context_data(self, **kwargs): + context = super(CreateProfileView, self).get_context_data(**kwargs) + context['title'] = 'Create Profile' + context['form'] = UpdateForm() + context['year'] = datetime.now().year + return context + + def post(self, request, *args, **kwargs): + profile_form = UpdateForm(request.POST, request.FILES) # Include request.FILES for file uploads + if profile_form.is_valid(): + profile, created = Profile.objects.get_or_create(user=request.user) + for field, value in profile_form.cleaned_data.items(): + if field == 'profile_image' and not value: + continue # Skip updating profile_image if no new file is provided + setattr(profile, field, value) + profile.save() + return redirect('profiles:profile_home') # Ensure you have the correct redirect URL + else: + return render(request, self.template_name, {'form': profile_form}) + +class UpdateProfileView(UpdateView): + form_class = UpdateForm + model = Profile + template_name = "profiles/update.html" # Ensure this template path is correct + success_url = reverse_lazy('registration:profile_update') # Adjust the success URL as needed + + def get_context_data(self, **kwargs): + context = super(UpdateProfileView, self).get_context_data(**kwargs) + context['title'] = 'Update Profile' + context['year'] = datetime.now().year + try: + context['profile'] = Profile.objects.get(user=self.request.user) # Use get() for a single object + except Profile.DoesNotExist: + context['profile'] = None + return context + + def form_valid(self, form): + profile = form.save(commit=False) + profile.user = self.request.user + profile.user.name = profile.name + profile.user.last_name = profile.surname + profile.user.save() + profile.save() + return super(UpdateProfileView, self).form_valid(form) + + +class UpdateLoginView(EditOwnLoginMixin, UpdateView): + form_class = UserForm + model = User + template_name = "profiles/login_details.html" + success_url = reverse_lazy('registration:profile_update') + + def get_context_data(self, **kwargs): + context = super(UpdateLoginView, self).get_context_data(**kwargs) + try: + profile = User.objects.get(pk=self.request.user.pk) + except User.DoesNotExist: + context['profile'] = '' + + else: + context['profile'] = profile + context['title'] = 'Update Login Details' + context['year'] = datetime.now().year + return context + + +class PasswordView(UpdateView): + model = User + form_class = PasswordForm + template_name = "profiles/password_change.html" + success_url = reverse_lazy('registration:profile_update') + + def get_context_data(self, **kwargs): + context = super(PasswordView, self).get_context_data(**kwargs) + context['title'] = 'Change Password' + try: + profile = get_object_or_404(User, pk=self.request.user.pk) + except User.DoesNotExist: + context['profile'] = '' + return HttpResponseRedirect('registration:create_profile') + + else: + context['profile'] = profile + context['year'] = datetime.now().year + return context + + +class SuccessView(TemplateView): + template_name = "profiles/success.html" + + def get_context_data(self, **kwargs): + context = super(SuccessView, self).get_context_data(**kwargs) + context['title'] = 'Profile Update Successful' + context['year'] = datetime.now().year + return context + + + + + + + + + + + + + + diff --git a/pycons-site/schedule/__init__.py b/pycons-site/schedule/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/schedule/serializers.py b/pycons-site/schedule/serializers.py new file mode 100644 index 0000000..15f65ff --- /dev/null +++ b/pycons-site/schedule/serializers.py @@ -0,0 +1,28 @@ +from rest_framework import serializers + +from conference_schedule.models import Schedule, Event, Day +from talks.models import Proposal + + +class DaySerializer(serializers.ModelSerializer): + class Meta: + model = Day + fields = ('conference_day') + + +class TalkScheduleSerializer(serializers.ModelSerializer): + class Meta: + model = Schedule + fields = all + + +class TalkSerializer(serializers.ModelSerializer): + class Meta: + model = Proposal + exclude = ('notes', 'talk_abstract', 'status') + + +class EventSerializer(serializers.ModelSerializer): + class Meta: + model = Event + fields = all diff --git a/pycons-site/schedule/templatetags/__init__.py b/pycons-site/schedule/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/schedule/templatetags/schedule_tags.py b/pycons-site/schedule/templatetags/schedule_tags.py new file mode 100644 index 0000000..db7f0e1 --- /dev/null +++ b/pycons-site/schedule/templatetags/schedule_tags.py @@ -0,0 +1,62 @@ +from django import template +from conference_schedule.models import Schedule, Day, ScheduleVisibility +from home.models import EventYear +from django.template.loader import get_template +from django.template import TemplateDoesNotExist +from django import template +from django.template.loader import get_template, TemplateDoesNotExist +from conference_schedule.models import ScheduleVisibility, Schedule, Day + +register = template.Library() + + +@register.inclusion_tag('2024/schedule/schedule_home.html', takes_context=True) +def schedule_preview(context, year, limit=3): + request = context['request'] + event_year = EventYear.objects.get(year=year) + + # Check visibility of the schedule + visibility = ScheduleVisibility.objects.first() + if visibility is None: + visibility = ScheduleVisibility.objects.create(is_live=False) + + # If the schedule is not live and the user is not a superuser, return an empty schedule + if not visibility.is_live and not request.user.is_superuser: + return { + 'day_schedules': [], + 'year': year, + 'limit': limit, + 'is_schedule_live': visibility.is_live or request.user.is_superuser, + } + + # Fetch all conference days + days = Day.objects.all().order_by('conference_day') + day_schedules = [] + + for day in days: + # Fetch both talks and events for each day, limited to the first `limit` items + schedules = Schedule.objects.filter( + conference_day=day + ).select_related('talk', 'talk__user').prefetch_related('talk__speakers').order_by('start_time')[:limit] + + # Add each day along with its schedules (even if there are no schedules) + day_schedules.append({ + 'day': day, + 'schedules': schedules + }) + + # Attempt to get the correct template for the year, fallback to 'partials/schedule_home.html' if it doesn't exist + template_path = f'{year}/schedule/schedule_home.html' + try: + get_template(template_path) + except TemplateDoesNotExist: + template_path = 'partials/schedule_home.html' + + return { + 'day_schedules': day_schedules, + 'year': year, + 'days': days, + 'limit': limit, + 'is_schedule_live': visibility.is_live, + 'template_path': template_path, + } \ No newline at end of file diff --git a/pycons-site/speakers/__init__.py b/pycons-site/speakers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/speakers/admin.py b/pycons-site/speakers/admin.py new file mode 100644 index 0000000..cabdd4d --- /dev/null +++ b/pycons-site/speakers/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin +from .models import * + + + \ No newline at end of file diff --git a/pycons-site/speakers/apps.py b/pycons-site/speakers/apps.py new file mode 100644 index 0000000..37d7aec --- /dev/null +++ b/pycons-site/speakers/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SpeakersConfig(AppConfig): + name = 'speakers' diff --git a/pycons-site/speakers/forms.py b/pycons-site/speakers/forms.py new file mode 100644 index 0000000..06901eb --- /dev/null +++ b/pycons-site/speakers/forms.py @@ -0,0 +1,9 @@ +from django import forms + +from registration.models import Profile + +class SpeakerForm(forms.ModelForm): + + class Meta: + model = Profile + fields = ('name', 'biography') diff --git a/pycons-site/speakers/models.py b/pycons-site/speakers/models.py new file mode 100644 index 0000000..361fcd1 --- /dev/null +++ b/pycons-site/speakers/models.py @@ -0,0 +1,3 @@ +from django.conf import settings +from django.db import models +from django.utils import timezone diff --git a/pycons-site/speakers/templatetags/__init__.py b/pycons-site/speakers/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/speakers/templatetags/only_hours.py b/pycons-site/speakers/templatetags/only_hours.py new file mode 100644 index 0000000..ac7319d --- /dev/null +++ b/pycons-site/speakers/templatetags/only_hours.py @@ -0,0 +1,14 @@ +from django import template +from django.template.defaultfilters import stringfilter + +register = template.Library() + + +@register.filter +@stringfilter +def upto(value, delimiter=None): + return value.split(delimiter)[0] + + +upto.is_safe = True + diff --git a/pycons-site/speakers/templatetags/speakers_tags.py b/pycons-site/speakers/templatetags/speakers_tags.py new file mode 100644 index 0000000..5d2e552 --- /dev/null +++ b/pycons-site/speakers/templatetags/speakers_tags.py @@ -0,0 +1,10 @@ +from django import template + +register = template.Library() +from registration.models import Profile + + +@register.inclusion_tag('2022/speakers/speakers_home.html') +def show_home_speaker(): + speaker_h_home = Profile.objects.filter(is_visible=True).order_by('date_created') + return {'speaker_h_home': speaker_h_home} diff --git a/pycons-site/speakers/templatetags/urlify.py b/pycons-site/speakers/templatetags/urlify.py new file mode 100644 index 0000000..9b9d27f --- /dev/null +++ b/pycons-site/speakers/templatetags/urlify.py @@ -0,0 +1,14 @@ +# Third-party library imports +import urllib.parse as encode + +# Core Django imports +from django import template + +register = template.Library() + + +@register.filter +def urlify(value): + encode_url = encode.quote_plus(value) + return encode_url + diff --git a/pycons-site/speakers/tests.py b/pycons-site/speakers/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/speakers/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/speakers/urls.py b/pycons-site/speakers/urls.py new file mode 100644 index 0000000..6b8cc9d --- /dev/null +++ b/pycons-site/speakers/urls.py @@ -0,0 +1,15 @@ +from django.urls import path, include +from . import views +from .views import * + + + + +app_name = 'speakers' +urlpatterns = [ + path('', Speakers.as_view(), name='speakers'), + path('/', SpeakerDetailView.as_view(), name='speaker_detail'), + path('hitcount/', include(('hitcount.urls', 'hitcount'), namespace='hitcount')), +] + + diff --git a/pycons-site/speakers/views.py b/pycons-site/speakers/views.py new file mode 100644 index 0000000..a36075e --- /dev/null +++ b/pycons-site/speakers/views.py @@ -0,0 +1,132 @@ +from django.views.generic import ListView +from django.shortcuts import get_object_or_404 +from hitcount.views import HitCountDetailView +from django.utils.text import Truncator +from registration.models import Profile +from talks.models import Proposal +from conference_schedule.models import Schedule + +from home.models import EventYear +from event.models import Event +from django.db.models import Q, Exists, OuterRef, Case, When, IntegerField + + +class Speakers(ListView): + model = Profile + context_object_name = 'speakers' + + def get_template_names(self): + year = self.kwargs.get('year', 'default') + return [f'{year}/speakers/speaker_list.html'] + + def get_queryset(self): + queryset = super().get_queryset() + + # Filter speakers based on accepted proposals and user response + queryset = queryset.filter( + Q(user__proposals__status='A', user__proposals__user_response='A') | + Q(user__speaking_proposals__status='A', user__speaking_proposals__user_response='A') + ).annotate( + is_keynote_speaker=Exists( + Proposal.objects.filter( + user=OuterRef('user'), + talk_type="Keynote Speaker", + status='A', + user_response='A' + ) + ), + sort_priority=Case( + When(user__proposals__talk_type="Sponsored Talk", then=1), + When(user__proposals__talk_type__in=["Short Talk", "Long Talk"], then=2), + When(user__proposals__talk_type="Lightning Talk", then=3), + default=4, + output_field=IntegerField(), + ) + ).order_by('sort_priority', 'user', 'date_created') + + # Remove duplicate speakers + seen_users = set() + unique_speakers = [] + for speaker in queryset: + if speaker.user_id not in seen_users: + seen_users.add(speaker.user_id) + unique_speakers.append(speaker) + + return unique_speakers + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + speakers = context['speakers'] + keynote_speakers = [speaker for speaker in speakers if speaker.is_keynote_speaker] + other_speakers = [speaker for speaker in speakers if not speaker.is_keynote_speaker] + + # Meta Information + meta_description = "Meet the incredible speakers at PyCon Africa. From keynotes to sponsored talks, our speakers bring a wealth of knowledge and insight to the stage." + meta_title = f"Speakers at PyCon Africa {self.kwargs.get('year', 'default')}" if keynote_speakers else f"Speakers at PyCon Africa {self.kwargs.get('year', 'default')}" + + context.update({ + 'keynote_speakers': keynote_speakers, + 'other_speakers': other_speakers, + 'meta_title': meta_title, + 'meta_description': meta_description, + }) + return context + + + + +class SpeakerDetailView(HitCountDetailView): + model = Profile + context_object_name = 'speaker' + slug_field = 'profile_id' + pk_url_kwarg = 'profile_id' + count_hit = True + + def get_template_names(self): + year = self.kwargs.get('year', 'default') + return [f'{year}/speakers/speaker_details.html'] + + def get_context_data(self, **kwargs): + context = super(SpeakerDetailView, self).get_context_data(**kwargs) + year = self.kwargs.get('year') + event_year = get_object_or_404(EventYear, year=year) + + # Get accepted talks for the speaker + talks = Proposal.objects.filter(user=self.object.user, status="A", event_year=event_year) + + # Collect related speakers without duplication + related_speakers = Profile.objects.filter( + user__proposals__status='A', + user__proposals__event_year=event_year + ).annotate( + user_accepted=Exists( + Proposal.objects.filter( + user=OuterRef('user'), + user_response='A', + status='A', + event_year=event_year + ) + ) + ).filter(user_accepted=True).exclude(profile_id=self.object.profile_id).distinct() + + # Truncate biography to 30 words + truncated_biography = Truncator(self.object.biography).words(50, truncate='...') + + # Meta tags information + meta_title = f"{self.object.name} {self.object.surname} | PyCon Africa {year}" + meta_description = f"Meet {self.object.name}, a speaker at PyCon Africa {year}. {truncated_biography}" + meta_author = "PyCon Africa" + meta_og_image = self.object.profile_image.url if self.object.profile_image else "https://res.cloudinary.com/pycon-africa/image/upload/v1722977619/website_storage_location/media/pyconafrica.png" + + context.update({ + 'talks': talks, + 'related_speakers': related_speakers, + 'events': Event.objects.all(), + 'speakers': Profile.objects.filter(is_visible=True), + 'schedule': Schedule.objects.all(), + 'meta_title': meta_title, + 'meta_description': meta_description, + 'meta_author': meta_author, + 'meta_og_image': meta_og_image, + }) + return context \ No newline at end of file diff --git a/pycons-site/sponsor_us/__init__.py b/pycons-site/sponsor_us/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/sponsor_us/admin.py b/pycons-site/sponsor_us/admin.py new file mode 100644 index 0000000..e05bbbb --- /dev/null +++ b/pycons-site/sponsor_us/admin.py @@ -0,0 +1,59 @@ +# Core Django imports. +from django.contrib import admin +from .models import * +from markdownx.admin import MarkdownxModelAdmin + + +class SponsorshipBenefitInline(admin.StackedInline): + model = SponsorshipBenefit + extra = 1 + +class AdditionalResourceInline(admin.StackedInline): + model = AdditionalResource + extra = 1 + + +@admin.register(SponsorUsPage) +class SponsorUsPageAdmin(MarkdownxModelAdmin): + list_display = ('title', 'user', 'event_year', 'date_created', 'date_updated') + exclude = ('user',) # Exclude the user field from the admin form + + def save_model(self, request, obj, form, change): + if not obj.pk: # If the object is being created (and not modified) + obj.user = request.user # Set the user to the current user + super().save_model(request, obj, form, change) + +@admin.register(SponsorshipTier) +class SponsorshipTierAdmin(MarkdownxModelAdmin): + list_display = ('name', 'show_in_grid', 'display_order', 'amount', 'colour', 'hex', 'date_created', 'date_updated') + list_editable = ('show_in_grid',) + inlines = [SponsorshipBenefitInline, AdditionalResourceInline] + search_fields = ['name',] + exclude = ('user',) # Exclude the user field from the admin form + + def save_model(self, request, obj, form, change): + if not obj.pk: # If the object is being created (and not modified) + obj.user = request.user # Set the user to the current user + super().save_model(request, obj, form, change) + +# You may or may not need to use MarkdownxModelAdmin for SponsorshipBenefit +# and AdditionalResource if they do not contain MarkdownxFields. +class SponsorshipBenefitAdmin(admin.ModelAdmin): + list_display = ('tier_display', 'description_short') + + def tier_display(self, obj): + return str(obj.tier) + tier_display.short_description = 'Tier' + + def description_short(self, obj): + return (obj.description[:75] + '...') if len(obj.description) > 75 else obj.description + description_short.short_description = 'Description' + +admin.site.register(SponsorshipBenefit, SponsorshipBenefitAdmin) + +@admin.register(AdditionalResource) +class AdditionalResourceAdmin(admin.ModelAdmin): + list_display = ('sponsorship_tier', 'package') + + + diff --git a/pycons-site/sponsor_us/apps.py b/pycons-site/sponsor_us/apps.py new file mode 100644 index 0000000..cd3da77 --- /dev/null +++ b/pycons-site/sponsor_us/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SponsorUsConfig(AppConfig): + name = 'sponsor_us' diff --git a/pycons-site/sponsor_us/migrations/0001_initial.py b/pycons-site/sponsor_us/migrations/0001_initial.py new file mode 100644 index 0000000..763f1a4 --- /dev/null +++ b/pycons-site/sponsor_us/migrations/0001_initial.py @@ -0,0 +1,47 @@ +# Generated by Django 5.0.2 on 2024-03-05 15:54 + +import django.db.models.deletion +import markdownx.models +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('home', '0008_pyconevent'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='SponsorshipTier', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Sponsorship Tier name. eg, Gold, Silver', max_length=100)), + ('amount', models.CharField(blank=True, help_text='Sponsorship Tier Amount eg, $1000', max_length=25)), + ('no_needed', models.CharField(blank=True, help_text='Sponsorship Tier needed', max_length=25)), + ('no_available', models.CharField(blank=True, help_text='Sponsorship Tier available', max_length=25)), + ('colour', models.CharField(default='#000', help_text='Shadow colour for tier eg, #cd7f32', max_length=25)), + ('details', markdownx.models.MarkdownxField(help_text='[Supports Markdown] - Sponsorship Tier.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sponsorship_tier_users', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='SponsorUsPage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Support PyCon Africa', max_length=250)), + ('why_sponsor_us', markdownx.models.MarkdownxField(help_text='[Supports Markdown] - Support PyCon Africa.')), + ('special_sponsorship', markdownx.models.MarkdownxField(help_text='[Supports Markdown] - Special sponsorship package.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('event_year', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supportus_pages', to='home.eventyear')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supportus_pages', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/pycons-site/sponsor_us/migrations/0002_sponsorshiptier_display_order_and_more.py b/pycons-site/sponsor_us/migrations/0002_sponsorshiptier_display_order_and_more.py new file mode 100644 index 0000000..ea283f6 --- /dev/null +++ b/pycons-site/sponsor_us/migrations/0002_sponsorshiptier_display_order_and_more.py @@ -0,0 +1,63 @@ +# Generated by Django 5.0.2 on 2024-03-05 18:11 + +import django.db.models.deletion +import markdownx.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sponsor_us', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='sponsorshiptier', + name='display_order', + field=models.IntegerField(default=0, help_text='Order in which the tier should be displayed.'), + ), + migrations.AddField( + model_name='sponsoruspage', + name='prospectus_link', + field=models.URLField(blank=True, help_text='Link to the sponsorship prospectus.'), + ), + migrations.AlterField( + model_name='sponsorshiptier', + name='amount', + field=models.DecimalField(decimal_places=2, help_text='Sponsorship Tier Amount eg, 1000.00', max_digits=10), + ), + migrations.AlterField( + model_name='sponsorshiptier', + name='details', + field=markdownx.models.MarkdownxField(help_text='[Supports Markdown] - Detailed description of the tier.'), + ), + migrations.AlterField( + model_name='sponsorshiptier', + name='no_available', + field=models.IntegerField(help_text='Number of sponsorships available.'), + ), + migrations.AlterField( + model_name='sponsorshiptier', + name='no_needed', + field=models.IntegerField(help_text='Number of sponsorships needed.'), + ), + migrations.CreateModel( + name='AdditionalResource', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255)), + ('file', models.FileField(upload_to='sponsorship_resources/')), + ('description', models.TextField(blank=True)), + ('sponsorship_tier', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='additional_resources', to='sponsor_us.sponsorshiptier')), + ], + ), + migrations.CreateModel( + name='SponsorshipBenefit', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('description', models.CharField(max_length=255)), + ('tier', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='benefits', to='sponsor_us.sponsorshiptier')), + ], + ), + ] diff --git a/pycons-site/sponsor_us/migrations/0003_alter_sponsorshiptier_amount.py b/pycons-site/sponsor_us/migrations/0003_alter_sponsorshiptier_amount.py new file mode 100644 index 0000000..6cca962 --- /dev/null +++ b/pycons-site/sponsor_us/migrations/0003_alter_sponsorshiptier_amount.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-03-05 18:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sponsor_us', '0002_sponsorshiptier_display_order_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='sponsorshiptier', + name='amount', + field=models.IntegerField(help_text='Sponsorship Tier Amount eg, 1000.00'), + ), + ] diff --git a/pycons-site/sponsor_us/migrations/0004_remove_additionalresource_file.py b/pycons-site/sponsor_us/migrations/0004_remove_additionalresource_file.py new file mode 100644 index 0000000..23a1b01 --- /dev/null +++ b/pycons-site/sponsor_us/migrations/0004_remove_additionalresource_file.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.2 on 2024-03-05 18:22 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('sponsor_us', '0003_alter_sponsorshiptier_amount'), + ] + + operations = [ + migrations.RemoveField( + model_name='additionalresource', + name='file', + ), + ] diff --git a/pycons-site/sponsor_us/migrations/0005_rename_description_additionalresource_package_and_more.py b/pycons-site/sponsor_us/migrations/0005_rename_description_additionalresource_package_and_more.py new file mode 100644 index 0000000..e18b592 --- /dev/null +++ b/pycons-site/sponsor_us/migrations/0005_rename_description_additionalresource_package_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 5.0.2 on 2024-03-05 18:26 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('sponsor_us', '0004_remove_additionalresource_file'), + ] + + operations = [ + migrations.RenameField( + model_name='additionalresource', + old_name='description', + new_name='package', + ), + migrations.RemoveField( + model_name='additionalresource', + name='name', + ), + ] diff --git a/pycons-site/sponsor_us/migrations/0006_alter_sponsorshiptier_no_available.py b/pycons-site/sponsor_us/migrations/0006_alter_sponsorshiptier_no_available.py new file mode 100644 index 0000000..61ea549 --- /dev/null +++ b/pycons-site/sponsor_us/migrations/0006_alter_sponsorshiptier_no_available.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-03-15 17:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sponsor_us', '0005_rename_description_additionalresource_package_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='sponsorshiptier', + name='no_available', + field=models.IntegerField(blank=True, help_text='Number of sponsorships available.', null=True), + ), + ] diff --git a/pycons-site/sponsor_us/migrations/0007_sponsorshiptier_event_year.py b/pycons-site/sponsor_us/migrations/0007_sponsorshiptier_event_year.py new file mode 100644 index 0000000..b1fed5e --- /dev/null +++ b/pycons-site/sponsor_us/migrations/0007_sponsorshiptier_event_year.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.2 on 2024-03-17 16:31 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0008_pyconevent'), + ('sponsor_us', '0006_alter_sponsorshiptier_no_available'), + ] + + operations = [ + migrations.AddField( + model_name='sponsorshiptier', + name='event_year', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sponsorship_tiers', to='home.eventyear'), + ), + ] diff --git a/pycons-site/sponsor_us/migrations/0008_sponsorshiptier_hex_alter_sponsorshiptier_colour.py b/pycons-site/sponsor_us/migrations/0008_sponsorshiptier_hex_alter_sponsorshiptier_colour.py new file mode 100644 index 0000000..7ed6edc --- /dev/null +++ b/pycons-site/sponsor_us/migrations/0008_sponsorshiptier_hex_alter_sponsorshiptier_colour.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.2 on 2024-03-17 17:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sponsor_us', '0007_sponsorshiptier_event_year'), + ] + + operations = [ + migrations.AddField( + model_name='sponsorshiptier', + name='hex', + field=models.CharField(default='#000', help_text='Shadow colour for tier eg, #cd7f32', max_length=25), + ), + migrations.AlterField( + model_name='sponsorshiptier', + name='colour', + field=models.CharField(default='primary', help_text='Colour for tier eg, yellow, gray, primary etc', max_length=25), + ), + ] diff --git a/pycons-site/sponsor_us/migrations/0009_alter_sponsorshiptier_hex.py b/pycons-site/sponsor_us/migrations/0009_alter_sponsorshiptier_hex.py new file mode 100644 index 0000000..7be4473 --- /dev/null +++ b/pycons-site/sponsor_us/migrations/0009_alter_sponsorshiptier_hex.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-04-09 21:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sponsor_us', '0008_sponsorshiptier_hex_alter_sponsorshiptier_colour'), + ] + + operations = [ + migrations.AlterField( + model_name='sponsorshiptier', + name='hex', + field=models.CharField(default='#000', help_text='Hex colour for tier eg, #cd7f32', max_length=25), + ), + ] diff --git a/pycons-site/sponsor_us/migrations/__init__.py b/pycons-site/sponsor_us/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/sponsor_us/models.py b/pycons-site/sponsor_us/models.py new file mode 100644 index 0000000..13e4dbc --- /dev/null +++ b/pycons-site/sponsor_us/models.py @@ -0,0 +1,53 @@ +from django.db import models +from django.urls import reverse +from django.conf import settings +from django.contrib.auth.models import User +from markdownx.models import MarkdownxField +from django.utils import timezone +from home.models import EventYear + + +class SponsorUsPage(models.Model): + title = models.CharField(max_length=250, help_text='Support PyCon Africa') + why_sponsor_us = MarkdownxField(help_text="[Supports Markdown] - Support PyCon Africa.") + special_sponsorship = MarkdownxField(help_text="[Supports Markdown] - Special sponsorship package.") + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='supportus_pages') + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, related_name='supportus_pages') + prospectus_link = models.URLField(blank=True, help_text="Link to the sponsorship prospectus.") + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title + + +class SponsorshipBenefit(models.Model): + description = MarkdownxField(default='', help_text = "[Supports Markdown] - Full Benefit in .md", null=False, blank=False) + tier = models.ForeignKey('SponsorshipTier', on_delete=models.CASCADE, related_name='benefits') + + def __str__(self): + return self.description + + +class SponsorshipTier(models.Model): + name = models.CharField(max_length=100, help_text="Sponsorship Tier name. eg, Gold, Silver") + amount = models.IntegerField(help_text="Sponsorship Tier Amount eg, 1000.00") + colour = models.CharField(max_length=25, default="primary", help_text="Colour for tier eg, yellow, gray, primary etc") + hex = models.CharField(max_length=25, default="#000", help_text="Hex colour for tier eg, #cd7f32") + details = MarkdownxField(help_text="[Supports Markdown] - Detailed description of the tier.") + display_order = models.IntegerField(default=0, help_text="Order in which the tier should be displayed.") + show_in_grid = models.BooleanField(default=False, help_text="Tick this if the sponsors in this tier should be displayed in a grid layout.") + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, related_name='sponsorship_tiers', null=True) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sponsorship_tier_users') + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + def __str__(self): + return f"{self.name} Tier" + +class AdditionalResource(models.Model): + sponsorship_tier = models.ForeignKey(SponsorshipTier, on_delete=models.CASCADE, related_name='additional_resources') + package = models.TextField(blank=True) + + def __str__(self): + return str(self.sponsorship_tier) \ No newline at end of file diff --git a/pycons-site/sponsor_us/tests.py b/pycons-site/sponsor_us/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/sponsor_us/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/sponsor_us/urls.py b/pycons-site/sponsor_us/urls.py new file mode 100644 index 0000000..54402cf --- /dev/null +++ b/pycons-site/sponsor_us/urls.py @@ -0,0 +1,10 @@ +from django.urls import path +from . import views + +app_name = 'sponsor_us' + + +urlpatterns = [ + path('', views.sponsor_us, name='sponsor_us'), + path('thank-you', views.thank_you, name='thank_you') +] \ No newline at end of file diff --git a/pycons-site/sponsor_us/views.py b/pycons-site/sponsor_us/views.py new file mode 100644 index 0000000..02dc9f2 --- /dev/null +++ b/pycons-site/sponsor_us/views.py @@ -0,0 +1,36 @@ +from __future__ import absolute_import +from django.shortcuts import render, reverse +from .models import * +from django.http import HttpRequest, HttpResponseRedirect +from django.shortcuts import render, get_object_or_404, redirect + + + + +# Create your views here. +def sponsor_us(request, year): + # Fetch the EventYear instance or raise a 404 error if not found + event_year = get_object_or_404(EventYear, year=year) + + # Fetch the SponsorUsPage for the specified year or raise a 404 error if not found + sponsor_us_page = get_object_or_404(SponsorUsPage, event_year=event_year) + + # Fetch all SponsorshipTier instances for the specified year, ordered by 'display_order' + sponsorship_tiers = SponsorshipTier.objects.filter(user=sponsor_us_page.user).order_by('display_order') + + # Building the context + context = { + 'event_year': event_year, + 'sponsor_us_page': sponsor_us_page, + 'sponsorship_tiers': sponsorship_tiers, + } + + # Dynamically building the template path based on the event year + template_name = f'{year}/sponsor-us/sponsor-us.html' + + return render(request, template_name, context) + +def thank_you(request): + context = {} + template = '2022/sponsor-us/thankyou.html' + return render(request, template, context) \ No newline at end of file diff --git a/pycons-site/sponsors/__init__.py b/pycons-site/sponsors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/sponsors/admin.py b/pycons-site/sponsors/admin.py new file mode 100644 index 0000000..0091904 --- /dev/null +++ b/pycons-site/sponsors/admin.py @@ -0,0 +1,40 @@ +from django.contrib import admin +from django.utils.html import format_html +from .models import Sponsor +from sponsor_us.models import SponsorshipTier +from home.models import EventYear + +class SponsorAdmin(admin.ModelAdmin): + list_display = ("logo_preview", "name", "is_visible", "show_biography", "sponsor_type", "get_tier_display", "get_event_year_display") + list_editable = ["is_visible", "show_biography"] + list_filter = ["is_visible", "sponsor_type", "tier__name", "event_year__year"] + search_fields = ['name', 'biography'] + # Removed autocomplete_fields to use dropdowns instead + # autocomplete_fields = ['tier', 'event_year'] + + def formfield_for_foreignkey(self, db_field, request, **kwargs): + if db_field.name == "tier": + kwargs["queryset"] = SponsorshipTier.objects.all().order_by('display_order') + if db_field.name == "event_year": + kwargs["queryset"] = EventYear.objects.all().order_by('-year') + return super().formfield_for_foreignkey(db_field, request, **kwargs) + + def get_tier_display(self, obj): + return obj.tier.name if obj.tier else '-' + get_tier_display.short_description = 'Tier' + + def get_event_year_display(self, obj): + return obj.event_year.year if obj.event_year else '-' + get_event_year_display.short_description = 'Event Year' + + # Method to show a preview of the logo + def logo_preview(self, obj): + if obj.logo: + return format_html('', obj.logo.url) + return "No Logo" + logo_preview.short_description = 'Logo Preview' + +admin.site.register(Sponsor, SponsorAdmin) + + + \ No newline at end of file diff --git a/pycons-site/sponsors/apps.py b/pycons-site/sponsors/apps.py new file mode 100644 index 0000000..4db5584 --- /dev/null +++ b/pycons-site/sponsors/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class SponsorsConfig(AppConfig): + name = 'sponsors' diff --git a/pycons-site/sponsors/migrations/0001_initial.py b/pycons-site/sponsors/migrations/0001_initial.py new file mode 100644 index 0000000..478a819 --- /dev/null +++ b/pycons-site/sponsors/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2 on 2022-08-23 02:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Sponsor', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200, verbose_name='sponsor name')), + ('category', models.CharField(blank=True, choices=[('Headline', 'Headline - ($8,000)'), ('Platinum', 'Platinum ($6,000)'), ('Diamond', 'Diamond ($3,500)'), ('Gold', 'Gold ($2,500)'), ('Silver', 'Silver - ($1,000)'), ('Bronze', 'Bronze - ($500)'), ('Diversity', 'Diversity'), ('Special', 'Special'), ('Individual', 'Individual'), ('Other', 'Other')], max_length=25, null=True)), + ('logo', models.ImageField(blank=True, max_length=255, null=True, upload_to='sponsors/')), + ('type', models.CharField(choices=[('C', 'Corporate Sponsor'), ('S', 'Special Sponsor'), ('D', 'Diversity'), ('I', 'Individual Sponsor')], max_length=1, verbose_name='sponsor type')), + ('is_visible', models.BooleanField(default=False)), + ('website', models.URLField(blank=True, default='', help_text='Link to Sponsor website')), + ('twitter', models.CharField(blank=True, default='', help_text="Please enter only the user name eg.'mawy_7' ", max_length=100, null=True)), + ('linkedin', models.CharField(blank=True, default='', help_text="Please enter only the user name eg.'mawy_7' ", max_length=100, null=True)), + ('biography', models.TextField(blank=True, default='', help_text='Description of the Sponsor', null=True)), + ('show_biography', models.BooleanField(default=True, help_text='Untick if the company only want their logo displayed on our website. Not all companies want their information on the site.')), + ], + options={ + 'managed': True, + }, + ), + ] diff --git a/pycons-site/sponsors/migrations/0002_alter_sponsor_show_biography.py b/pycons-site/sponsors/migrations/0002_alter_sponsor_show_biography.py new file mode 100644 index 0000000..4dcbf5d --- /dev/null +++ b/pycons-site/sponsors/migrations/0002_alter_sponsor_show_biography.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2 on 2022-09-02 10:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sponsors', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='sponsor', + name='show_biography', + field=models.BooleanField(default=False, help_text='Untick if the company only want their logo displayed on our website. Not all companies want their information on the site.'), + ), + ] diff --git a/pycons-site/sponsors/migrations/0003_alter_sponsor_category.py b/pycons-site/sponsors/migrations/0003_alter_sponsor_category.py new file mode 100644 index 0000000..d731ac3 --- /dev/null +++ b/pycons-site/sponsors/migrations/0003_alter_sponsor_category.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2 on 2022-10-04 07:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sponsors', '0002_alter_sponsor_show_biography'), + ] + + operations = [ + migrations.AlterField( + model_name='sponsor', + name='category', + field=models.CharField(blank=True, choices=[('Headline', 'Headline - ($8,000)'), ('Platinum', 'Platinum ($6,000)'), ('Diamond', 'Diamond ($3,500)'), ('Gold', 'Gold ($2,500)'), ('Silver', 'Silver - ($1,000)'), ('Bronze', 'Bronze - ($500)'), ('Diversity', 'Diversity'), ('Special', 'Special'), ('Community', 'Community'), ('Individual', 'Individual'), ('Other', 'Other')], max_length=25, null=True), + ), + ] diff --git a/pycons-site/sponsors/migrations/0004_alter_sponsor_id.py b/pycons-site/sponsors/migrations/0004_alter_sponsor_id.py new file mode 100644 index 0000000..f2c1c54 --- /dev/null +++ b/pycons-site/sponsors/migrations/0004_alter_sponsor_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-02-29 23:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sponsors', '0003_alter_sponsor_category'), + ] + + operations = [ + migrations.AlterField( + model_name='sponsor', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/pycons-site/sponsors/migrations/0005_rename_type_sponsor_sponsor_type_and_more.py b/pycons-site/sponsors/migrations/0005_rename_type_sponsor_sponsor_type_and_more.py new file mode 100644 index 0000000..8e0c262 --- /dev/null +++ b/pycons-site/sponsors/migrations/0005_rename_type_sponsor_sponsor_type_and_more.py @@ -0,0 +1,45 @@ +# Generated by Django 5.0.2 on 2024-03-17 01:23 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0008_pyconevent'), + ('sponsor_us', '0006_alter_sponsorshiptier_no_available'), + ('sponsors', '0004_alter_sponsor_id'), + ] + + operations = [ + migrations.RenameField( + model_name='sponsor', + old_name='type', + new_name='sponsor_type', + ), + migrations.RemoveField( + model_name='sponsor', + name='category', + ), + migrations.AddField( + model_name='sponsor', + name='event_year', + field=models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='sponsors', to='home.eventyear'), + ), + migrations.AddField( + model_name='sponsor', + name='tier', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='sponsor_us.sponsorshiptier', verbose_name='sponsorship tier'), + ), + migrations.AlterField( + model_name='sponsor', + name='linkedin', + field=models.CharField(blank=True, default='', help_text="Please enter only the user name eg.'mawy_7'", max_length=100, null=True), + ), + migrations.AlterField( + model_name='sponsor', + name='twitter', + field=models.CharField(blank=True, default='', help_text="Please enter only the user name eg.'mawy_7'", max_length=100, null=True), + ), + ] diff --git a/pycons-site/sponsors/migrations/0006_sponsor_slug.py b/pycons-site/sponsors/migrations/0006_sponsor_slug.py new file mode 100644 index 0000000..a8ebb32 --- /dev/null +++ b/pycons-site/sponsors/migrations/0006_sponsor_slug.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-03-17 01:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('sponsors', '0005_rename_type_sponsor_sponsor_type_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='sponsor', + name='slug', + field=models.SlugField(blank=True, unique=True), + ), + ] diff --git a/pycons-site/sponsors/migrations/__init__.py b/pycons-site/sponsors/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/sponsors/models.py b/pycons-site/sponsors/models.py new file mode 100644 index 0000000..715705d --- /dev/null +++ b/pycons-site/sponsors/models.py @@ -0,0 +1,50 @@ +from django.db import models +from django.template.defaultfilters import slugify +from django.urls import reverse +from sponsor_us.models import SponsorshipTier +from home.models import EventYear +from markdownx.models import MarkdownxField + +class Sponsor(models.Model): + SPONSOR_TYPE = ( + ('C', 'Corporate Sponsor'), + ('S', 'Special Sponsor'), + ('G', 'Grant'), + ('D', 'Diversity'), + ('I', 'Individual Sponsor'), + ) + + name = models.CharField("sponsor name", max_length=200) + tier = models.ForeignKey(SponsorshipTier, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="sponsorship tier") + logo = models.ImageField(upload_to="sponsors/", max_length=255, blank=True, null=True) + sponsor_type = models.CharField("sponsor type", max_length=1, choices=SPONSOR_TYPE) + is_visible = models.BooleanField(default=False) + website = models.URLField(default='', help_text='Link to Sponsor website', blank=True) + twitter = models.CharField(max_length=100, null=True, help_text="Please enter only the user name eg.'mawy_7'", default="", blank=True) + linkedin = models.CharField(max_length=100, null=True, help_text="Please enter only the user name eg.'mawy_7'", default="", blank=True) + biography = MarkdownxField(default='', help_text="Description of the Sponsor", blank=True, null=True) + show_biography = models.BooleanField(default=False, help_text="Untick if the company only wants their logo displayed on our website. Not all companies want their information on the site.") + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='sponsors') + slug = models.SlugField(unique=True, blank=True) + + class Meta: + managed = True + + def __str__(self): + return self.name + + def save(self, *args, **kwargs): + if not self.slug: + self.slug = slugify(self.name) + original_slug = self.slug + queryset = Sponsor.objects.filter(slug=self.slug) + count = 1 + # Ensure the slug is unique by appending a number if necessary + while queryset.exists(): + self.slug = f'{original_slug}-{count}' + count += 1 + queryset = Sponsor.objects.filter(slug=self.slug) + super().save(*args, **kwargs) + + def get_absolute_url(self): + return reverse('sponsor_detail', kwargs={'year': self.event_year.year, 'slug': self.slug}) diff --git a/pycons-site/sponsors/templatetags/__init__.py b/pycons-site/sponsors/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/sponsors/templatetags/sponsors_hours.py b/pycons-site/sponsors/templatetags/sponsors_hours.py new file mode 100644 index 0000000..ac7319d --- /dev/null +++ b/pycons-site/sponsors/templatetags/sponsors_hours.py @@ -0,0 +1,14 @@ +from django import template +from django.template.defaultfilters import stringfilter + +register = template.Library() + + +@register.filter +@stringfilter +def upto(value, delimiter=None): + return value.split(delimiter)[0] + + +upto.is_safe = True + diff --git a/pycons-site/sponsors/templatetags/sponsors_tags.py b/pycons-site/sponsors/templatetags/sponsors_tags.py new file mode 100644 index 0000000..ff70213 --- /dev/null +++ b/pycons-site/sponsors/templatetags/sponsors_tags.py @@ -0,0 +1,29 @@ +from django import template +from ..models import Sponsor +from django.template.loader import render_to_string + + +register = template.Library() + +@register.simple_tag(takes_context=True) +def show_sponsor(context): + sponsor = Sponsor.objects.filter(is_visible=True) + latest_year = sponsor.first().event_year.year if sponsor.exists() else 2024 + template_name = f'{latest_year}/sponsors/footer_sponsors.html' + return render_to_string(template_name, {'sponsor': sponsor, 'event_year': latest_year}, request=context['request']) + +@register.simple_tag(takes_context=True) +def show_home_sponsor(context): + sponsor_h_home = Sponsor.objects.filter(is_visible=True, tier__name="HEADLINE SPONSOR") + sponsor_p_home = Sponsor.objects.filter(is_visible=True, tier__name="PLATINUM") + sponsor_d_home = Sponsor.objects.filter(is_visible=True, tier__name="DIAMOND") + sponsor_g_home = Sponsor.objects.filter(is_visible=True, tier__name="GOLD") + latest_year = sponsor_h_home.first().event_year.year if sponsor_h_home.exists() else 2024 + template_name = f'{latest_year}/sponsors/home_sponsors.html' + return render_to_string(template_name, { + 'sponsor_h_home': sponsor_h_home, + 'sponsor_p_home': sponsor_p_home, + 'sponsor_d_home': sponsor_d_home, + 'sponsor_g_home': sponsor_g_home, + 'event_year': latest_year + }, request=context['request']) \ No newline at end of file diff --git a/pycons-site/sponsors/templatetags/sponsors_urlify.py b/pycons-site/sponsors/templatetags/sponsors_urlify.py new file mode 100644 index 0000000..9b9d27f --- /dev/null +++ b/pycons-site/sponsors/templatetags/sponsors_urlify.py @@ -0,0 +1,14 @@ +# Third-party library imports +import urllib.parse as encode + +# Core Django imports +from django import template + +register = template.Library() + + +@register.filter +def urlify(value): + encode_url = encode.quote_plus(value) + return encode_url + diff --git a/pycons-site/sponsors/tests.py b/pycons-site/sponsors/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/sponsors/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/sponsors/urls.py b/pycons-site/sponsors/urls.py new file mode 100644 index 0000000..e6374df --- /dev/null +++ b/pycons-site/sponsors/urls.py @@ -0,0 +1,9 @@ +from django.urls import path +from . import views + +app_name = 'sponsors' +urlpatterns = [ + path('', views.sponsors, name='sponsors'), + path('/', views.sponsor_detail, name='sponsor_detail'), + path('prospectus', views.Prospectus, name='Prospectus') +] diff --git a/pycons-site/sponsors/views.py b/pycons-site/sponsors/views.py new file mode 100644 index 0000000..eee97e3 --- /dev/null +++ b/pycons-site/sponsors/views.py @@ -0,0 +1,50 @@ +from __future__ import absolute_import +from .models import * +from django.shortcuts import render, get_object_or_404 +from sponsor_us.models import SponsorshipTier +# Create your views here. + + +def Prospectus(request): + context = {} + template = 'prospectus.html' + return render(request, template, context) + + +def sponsors(request, year): + event_year = get_object_or_404(EventYear, year=year) + sponsorship_tiers = SponsorshipTier.objects.filter(event_year=event_year).order_by('display_order') + + no_sponsors = True # Initialize as True, assuming no sponsors initially + + for tier in sponsorship_tiers: + tier.sponsors = Sponsor.objects.filter(tier=tier, is_visible=True) + if tier.sponsors.exists(): + no_sponsors = False # If any tier has visible sponsors, set no_sponsors to False + + return render(request, f'{year}/sponsors/sponsors.html', { + 'sponsorship_tiers': sponsorship_tiers, + 'event_year': event_year, + 'no_sponsors': no_sponsors, # Pass the no_sponsors variable to the template + }) + + + + +def sponsors_list(request, year): + event_year = get_object_or_404(EventYear, year=year) + sponsors = Sponsor.objects.filter(event_year=event_year, is_visible=True).order_by('sponsor_type', 'name') + context = { + 'sponsors': sponsors, + 'year': year, + } + return render(request, f'{year}/sponsors/list.html', context) + + +def sponsor_detail(request, year, slug): + sponsor = get_object_or_404(Sponsor, slug=slug, event_year__year=year) + context = { + 'sponsor': sponsor, + 'year': year, + } + return render(request, f'{year}/sponsors/detail.html', context) \ No newline at end of file diff --git a/pycons-site/talks/__init__.py b/pycons-site/talks/__init__.py new file mode 100644 index 0000000..528e0a5 --- /dev/null +++ b/pycons-site/talks/__init__.py @@ -0,0 +1 @@ +default_app_config = 'talks.apps.TalksConfig' diff --git a/pycons-site/talks/admin.py b/pycons-site/talks/admin.py new file mode 100644 index 0000000..8c9d328 --- /dev/null +++ b/pycons-site/talks/admin.py @@ -0,0 +1,240 @@ +# admin.py +from django.contrib import admin +from import_export import resources, fields, formats +from import_export.admin import ImportExportModelAdmin +from .models import * +from .forms import ExportFieldsForm +from django.shortcuts import render +from django.http import HttpResponseRedirect, HttpResponse +from django.urls import path, reverse +from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME + + +class ProposalResource(resources.ModelResource): + user_email = fields.Field(attribute='user__email', column_name='Email') + user_first_name = fields.Field(attribute='user__user_profile__name', column_name='First Name') + user_last_name = fields.Field(attribute='user__user_profile__surname', column_name='Last Name') + user_username = fields.Field(attribute='user__username', column_name='Username') + event_year = fields.Field(attribute='event_year__year', column_name='Event Year') + multiple_submissions = fields.Field(column_name='Multiple Submissions') + + class Meta: + model = Proposal + fields = ( + 'title', 'talk_type', 'talk_category', 'elevator_pitch', 'talk_abstract', 'user_email', 'user_first_name', + 'user_last_name', 'user_username', 'status', 'intended_audience', 'link_to_preview_video_url', + 'anything_else_you_want_to_tell_us', 'special_requirements', 'recording_release', 'youtube_video_url', + 'youtube_iframe_url', 'created_date', 'date_updated', 'event_year', 'multiple_submissions' + ) + + def dehydrate_multiple_submissions(self, proposal): + user_talk_count = Proposal.objects.filter( + user=proposal.user, + event_year=proposal.event_year + ).count() + return user_talk_count > 1 + +class TalkAdmin(ImportExportModelAdmin): + resource_class = ProposalResource + list_display = ("title", "user", 'list_speakers', "talk_type", "intended_audience", "status", "user_response", 'created_date', 'date_updated') + list_editable = ["status", "user_response"] + list_filter = ("talk_type", 'created_date', "status", "user_response") + actions = ['export_selected_action'] + + def get_urls(self): + urls = super().get_urls() + custom_urls = [ + path('export_selected/', self.admin_site.admin_view(self.export_selected), name='export_selected'), + ] + return custom_urls + urls + + def get_export_resource_class(self): + return self.resource_class + + def export_selected(self, request): + if request.method == 'POST': + form = ExportFieldsForm(request.POST) + if form.is_valid(): + fields_to_export = form.cleaned_data['fields_to_export'] + export_format = form.cleaned_data['export_format'] + selected_talk_types = form.cleaned_data['talk_types'] + + # Map the selected format to the import_export format class + format_map = { + 'csv': formats.base_formats.CSV, + 'xls': formats.base_formats.XLS, + 'xlsx': formats.base_formats.XLSX, + 'json': formats.base_formats.JSON, + 'yaml': formats.base_formats.YAML, + } + + selected_format_class = format_map[export_format] + queryset = self.get_queryset(request) + + if selected_talk_types: + queryset = queryset.filter(talk_type__in=selected_talk_types) + + resource = self.get_export_resource_class()() + + # Export only the selected fields + dataset = resource.export(queryset, fields=fields_to_export) + + export_data = selected_format_class().export_data(dataset) + response = HttpResponse(export_data, content_type=selected_format_class().get_content_type()) + response['Content-Disposition'] = f'attachment; filename="proposals.{selected_format_class().get_extension()}"' + return response + else: + form = ExportFieldsForm() + + context = { + 'form': form, + 'opts': self.model._meta, + 'app_label': self.model._meta.app_label, + } + return render(request, 'admin/export_selected.html', context) + + def export_selected_action(self, request, queryset): + selected_ids = request.POST.getlist(ACTION_CHECKBOX_NAME) + return HttpResponseRedirect(reverse('admin:export_selected') + '?' + '&'.join([f'id={id}' for id in selected_ids])) + + export_selected_action.short_description = "Export selected proposals" + + def list_speakers(self, obj): + return ", ".join([speaker.username for speaker in obj.speakers.all()]) + list_speakers.short_description = 'Speakers' + +admin.site.register(Proposal, TalkAdmin) + + + + + + + +class CFPSubmissionPeriodAdmin(admin.ModelAdmin): + list_display = ('event_year', 'start_date', 'end_date', 'is_active_period') + list_filter = ('event_year',) + search_fields = ('event_year__year',) + + def is_active_period(self, obj): + return obj.is_active() + is_active_period.boolean = True + is_active_period.short_description = 'Is active?' + +admin.site.register(CFPSubmissionPeriod, CFPSubmissionPeriodAdmin) + +class SpeakAdmin(admin.ModelAdmin): + list_display = ('title', 'date_created') + ordering = ['-date_created'] + +admin.site.register(Speak, SpeakAdmin) + +class SpeakerInvitationAdmin(admin.ModelAdmin): + list_display = ('talk', 'invitee', 'status', 'invitation_sent') + ordering = ['-invitation_sent'] + +admin.site.register(SpeakerInvitation, SpeakerInvitationAdmin) + +class ProposingTalkAdmin(admin.ModelAdmin): + list_display = ('title', 'date_created') + ordering = ['-date_created'] + +admin.site.register(Proposing_talk, ProposingTalkAdmin) + +class ReviewerAdmin(admin.ModelAdmin): + list_display = ['user', 'get_email'] + search_fields = ['user__username', 'user__email'] + + def get_email(self, obj): + return obj.user.email + get_email.admin_order_field = 'user__email' # Allows column order sorting + get_email.short_description = 'Email Address' # Renames column head + +admin.site.register(Reviewer, ReviewerAdmin) + +class SubScoreInline(admin.TabularInline): + model = SubScore + fields = ['speaker_expertise', 'depth_of_topic', 'relevancy', 'value_or_impact'] + readonly_fields = ['speaker_expertise', 'depth_of_topic', 'relevancy', 'value_or_impact'] + can_delete = False + extra = 0 + + + + +class ReviewAdmin(admin.ModelAdmin): + list_display = ['talk', 'get_reviewer_name', 'average_score', 'get_hashid', 'short_comment', 'has_multiple_submissions', 'number_of_submissions', 'rank_in_submissions'] + + def get_reviewer_name(self, obj): + return obj.reviewer.user.get_full_name() or obj.reviewer.user.username + get_reviewer_name.admin_order_field = 'reviewer__user__username' # Allows column order sorting by username + get_reviewer_name.short_description = 'Reviewer Name' # Renames column head + + def get_hashid(self, obj): + return str(obj.id) # Assuming 'id' is a Hashid field + get_hashid.admin_order_field = 'id' # Allows column order sorting + get_hashid.short_description = 'ID' # Renames column head + + def short_comment(self, obj): + return obj.comments[:50] + '...' if len(obj.comments) > 50 else obj.comments + short_comment.short_description = 'Comments' + + def average_score(self, obj): + return obj.average_score() # Assuming 'average_score' is a method on the Review model + average_score.admin_order_field = 'average_score' # Allows column order sorting + average_score.short_description = 'Average Score' + + def has_multiple_submissions(self, obj): + user = obj.talk.user + event_year = obj.talk.event_year + count = Proposal.objects.filter(user=user, event_year=event_year).count() + return count > 1 + has_multiple_submissions.short_description = 'Multiple Submissions' + has_multiple_submissions.boolean = True + + def number_of_submissions(self, obj): + user = obj.talk.user + event_year = obj.talk.event_year + return Proposal.objects.filter(user=user, event_year=event_year).count() + number_of_submissions.short_description = 'Number of Submissions' + + def rank_in_submissions(self, obj): + user = obj.talk.user + event_year = obj.talk.event_year + proposals = Proposal.objects.filter(user=user, event_year=event_year) + sorted_proposals = sorted(proposals, key=lambda p: p.average_review_score(), reverse=True) + rank = next((i for i, p in enumerate(sorted_proposals, 1) if p == obj.talk), None) + return rank + rank_in_submissions.short_description = 'Rank in Submissions' + + inlines = [SubScoreInline] + +admin.site.register(Review, ReviewAdmin) + + + + +class RecordingAdmin(admin.ModelAdmin): + list_display = ('title', 'date_created') + ordering = ['-date_created'] + +admin.site.register(Recording, RecordingAdmin) + + + +class DocumentAdmin(admin.ModelAdmin): + list_display = ('id', 'name', 'document_type', 'proposal', 'uploaded_at') + list_filter = ('document_type', 'uploaded_at', 'proposal__event_year') + search_fields = ('name', 'proposal__title', 'proposal__user__username') + date_hierarchy = 'uploaded_at' + ordering = ('-uploaded_at',) + + def proposal_title(self, obj): + return obj.proposal.title + proposal_title.short_description = 'Proposal Title' + + def proposal_user(self, obj): + return obj.proposal.user.username + proposal_user.short_description = 'Uploaded By' + +admin.site.register(Document, DocumentAdmin) \ No newline at end of file diff --git a/pycons-site/talks/apps.py b/pycons-site/talks/apps.py new file mode 100644 index 0000000..2c02a18 --- /dev/null +++ b/pycons-site/talks/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + +class TalksConfig(AppConfig): + name = 'talks' + + def ready(self): + pass diff --git a/pycons-site/talks/forms.py b/pycons-site/talks/forms.py new file mode 100644 index 0000000..ab6bb58 --- /dev/null +++ b/pycons-site/talks/forms.py @@ -0,0 +1,182 @@ +from django import forms +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Submit, Row, Column, Field +from django_recaptcha.fields import ReCaptchaField +from .models import * + +class ProposalForm(forms.ModelForm): + captcha = ReCaptchaField() + + class Meta: + model = Proposal + fields = ('title', 'talk_type', 'talk_category', 'intended_audience', 'elevator_pitch', 'talk_abstract', 'anything_else_you_want_to_tell_us', 'special_requirements', 'recording_release') + + def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) + super(ProposalForm, self).__init__(*args, **kwargs) + + if user and not user.is_staff: + self.fields['talk_type'].choices = [ + ('Lightning Talk', "Lightning Talk - 5 mins"), + ('Short Talk', "Short Talk - 30 mins"), + ('Long Talk', "Long Talk - 45 mins"), + ('Tutorial', "Tutorial - 2 hours"), + ] + + self.helper = FormHelper() + self.helper.form_id = 'id-Crispy_ProposalForm' + self.helper.form_class = 'form-horizontal' + self.helper.add_input(Submit('submit', 'Submit')) + + +class ProposalResponseForm(forms.ModelForm): + class Meta: + model = Proposal + fields = ['user_response'] + + +class UpdateForm(forms.ModelForm): + captcha = ReCaptchaField() + + class Meta: + model = Proposal + fields = ('title', 'talk_type', 'talk_category', 'intended_audience', 'talk_abstract', 'anything_else_you_want_to_tell_us', 'recording_release',) + + def __init__(self, *args, **kwargs): + super(UpdateForm, self).__init__(*args, **kwargs) + + self.helper = FormHelper() + self.helper.form_id = 'id-Crispy_UpdateForm' + self.helper.form_class = 'form-horizontal' + self.helper.add_input(Submit('update', 'Update Proposal')) + + +class ReviewForm(forms.ModelForm): + SCORE_CHOICES = [(i, i) for i in range(1, 6)] + + speaker_expertise = forms.ChoiceField(choices=SCORE_CHOICES, label="Speaker Expertise") + depth_of_topic = forms.ChoiceField(choices=SCORE_CHOICES, label="Depth of Topic") + relevancy = forms.ChoiceField(choices=SCORE_CHOICES, label="Relevancy") + value_or_impact = forms.ChoiceField(choices=SCORE_CHOICES, label="Value or Impact") + + class Meta: + model = Review + fields = ['speaker_expertise', 'depth_of_topic', 'relevancy', 'value_or_impact', 'comments'] + + + def __init__(self, *args, **kwargs): + super(ReviewForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_method = 'post' + self.helper.layout = Layout( + Row( + Column('speaker_expertise', css_class='form-control form-control-md form-control-lg rounded-0 g-mb-25'), + Column('depth_of_topic', css_class='form-control form-control-md form-control-lg rounded-0 g-mb-25'), + Column('relevancy', css_class='form-control form-control-md form-control-lg rounded-0 g-mb-25'), + Column('value_or_impact', css_class='form-control form-control-md form-control-lg rounded-0 g-mb-25'), + css_class='form-row' + ), + Row( + Column('comments', css_class='form-group col-md-12 mb-0'), + css_class='form-row' + ), + Submit('submit', 'Submit Review') + ) + + def save(self, commit=True): + instance = super(ReviewForm, self).save(commit=False) + if commit: + instance.save() + SubScore.objects.create( + review=instance, + speaker_expertise=self.cleaned_data['speaker_expertise'], + depth_of_topic=self.cleaned_data['depth_of_topic'], + relevancy=self.cleaned_data['relevancy'], + value_or_impact=self.cleaned_data['value_or_impact'] + ) + return instance + + +class DocumentForm(forms.ModelForm): + captcha = ReCaptchaField() + + class Meta: + model = Document + fields = ('name', 'document', 'document_type', 'proposal') + widgets = { + 'name': forms.TextInput(attrs={'placeholder': 'Brief name of the document'}), + 'document': forms.ClearableFileInput(attrs={'class': 'form-control-file'}), + 'document_type': forms.Select(attrs={'class': 'form-control'}), + 'proposal': forms.Select(attrs={'class': 'form-control', 'disabled': 'true'}), # Set to disabled + } + + def __init__(self, *args, **kwargs): + proposal = kwargs.pop('proposal', None) # Expecting 'proposal' to be passed as a kwarg + super().__init__(*args, **kwargs) + if proposal: + self.fields['proposal'].initial = proposal + self.fields['proposal'].disabled = True # Make the field read-only + self.helper = FormHelper() + self.helper.form_method = 'post' + self.helper.form_enctype = 'multipart/form-data' + self.helper.layout = Layout( + Field('name', css_class='form-control'), + Field('document', css_class='form-control-file'), + Field('document_type', css_class='form-control'), + Field('proposal', css_class='form-control'), + Submit('submit', 'Upload', css_class='btn btn-primary') + ) + + + +class ExportFieldsForm(forms.Form): + EXPORT_FIELDS_CHOICES = [ + ('title', 'Title'), + ('talk_type', 'Talk Type'), + ('talk_category', 'Talk Category'), + ('elevator_pitch', 'Elevator Pitch'), + ('talk_abstract', 'Talk Abstract'), + ('user_email', 'Email'), + ('user_first_name', 'First Name'), + ('user_last_name', 'Last Name'), + ('user_username', 'Username'), + ('status', 'Status'), + ('intended_audience', 'Intended Audience'), + ('link_to_preview_video_url', 'Link to Preview Video'), + ('anything_else_you_want_to_tell_us', 'Anything Else'), + ('special_requirements', 'Special Requirements'), + ('recording_release', 'Recording Release'), + ('youtube_video_url', 'YouTube Video URL'), + ('youtube_iframe_url', 'YouTube IFrame URL'), + ('created_date', 'Created Date'), + ('date_updated', 'Date Updated'), + ('event_year', 'Event Year'), + ('multiple_submissions', 'Multiple Submissions'), + ] + + EXPORT_FORMAT_CHOICES = [ + ('csv', 'CSV'), + ('xls', 'Excel (XLS)'), + ('xlsx', 'Excel (XLSX)'), + ('json', 'JSON'), + ('yaml', 'YAML'), + ] + + TALK_TYPE_CHOICES = Proposal.TALK_TYPES + + fields_to_export = forms.MultipleChoiceField( + choices=EXPORT_FIELDS_CHOICES, + widget=forms.CheckboxSelectMultiple, + required=True + ) + export_format = forms.ChoiceField( + choices=EXPORT_FORMAT_CHOICES, + required=True, + widget=forms.RadioSelect + ) + talk_types = forms.MultipleChoiceField( + choices=TALK_TYPE_CHOICES, + widget=forms.CheckboxSelectMultiple, + required=False, + label='Filter by Talk Type' + ) \ No newline at end of file diff --git a/pycons-site/talks/migrations/0001_initial.py b/pycons-site/talks/migrations/0001_initial.py new file mode 100644 index 0000000..088b1e3 --- /dev/null +++ b/pycons-site/talks/migrations/0001_initial.py @@ -0,0 +1,141 @@ +# Generated by Django 5.0.2 on 2024-09-06 14:11 + +import django.contrib.auth.models +import django.db.models.deletion +import django.utils.timezone +import hashid_field.field +import markdownx.models +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('home', '0009_alter_eventyear_id_alter_pyconevent_id'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='CFPSubmissionPeriod', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('start_date', models.DateTimeField(help_text='Date and time when proposal submissions start')), + ('end_date', models.DateTimeField(help_text='Date and time when proposal submissions end')), + ('event_year', models.ForeignKey(default='2024', help_text='The event year this submission period is for', on_delete=django.db.models.deletion.CASCADE, related_name='submission_periods', to='home.eventyear')), + ], + ), + migrations.CreateModel( + name='Proposal', + fields=[ + ('title', models.CharField(help_text='Public title. What topic/project is it all about?', max_length=1024)), + ('talk_type', models.CharField(choices=[('Lightning Talk', 'Lightning Talk - 5 mins'), ('Short Talk', 'Short Talk - 30 mins'), ('Long Talk', 'Long Talk - 45 mins'), ('Tutorial', 'Tutorial - 2 hours'), ('Sponsored Talk', 'Sponsored Talk'), ('Keynote Speaker', 'Keynote Speaker')], max_length=50)), + ('talk_category', models.CharField(choices=[('GP / Web', 'General Python, Web/DevOps'), ('GC', 'General Community'), ('ET', 'Emerging Technologies'), ('Education', 'Education'), ('O', 'Other')], max_length=50)), + ('proposal_id', hashid_field.field.HashidAutoField(alphabet='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', default=None, min_length=7, prefix='', primary_key=True, serialize=False)), + ('elevator_pitch', markdownx.models.MarkdownxField(blank=True, help_text='[Supports Markdown] - Describe your Talk to your targeted audience.', null=True)), + ('talk_abstract', markdownx.models.MarkdownxField(blank=True, help_text='[Supports Markdown] - Your talk_abstract.', null=True)), + ('status', models.CharField(choices=[('S', 'Submitted'), ('A', 'Accepted'), ('W', 'Waiting List'), ('R', 'Rejected'), ('RS', 'Rejected by Speaker')], default='S', max_length=2)), + ('intended_audience', models.CharField(blank=True, choices=[('BGN-L', 'Beginner Level'), ('INT-L', 'Intermediate Level'), ('EXP-L', 'Expert Level'), ('GEN-L', 'General')], max_length=50, null=True)), + ('link_to_preview_video_url', models.URLField(blank=True, help_text='Link to Preview video on your Youtube or Google drive')), + ('anything_else_you_want_to_tell_us', markdownx.models.MarkdownxField(blank=True, help_text='Kindly add anything else you want to tell us?', null=True)), + ('special_requirements', markdownx.models.MarkdownxField(blank=True, help_text='If you have any special requirements such as needing travel assistance, accessibility needs, or anything else please let us know here so that we may plan accordingly. (This is not made public nor will the review committee have access to view it.)', null=True)), + ('recording_release', models.BooleanField(default=True, help_text='By submitting your talk proposal, you agree to give permission to the conference organizers to record, edit, and release audio and/or video of your presentation. If you do not agree to this, please uncheck this box.')), + ('youtube_video_url', models.URLField(blank=True, help_text='Link to Talk on youtube Video')), + ('youtube_iframe_url', models.URLField(blank=True, help_text='Link to Youtube Iframe', max_length=300)), + ('user_response', models.CharField(choices=[('P', 'Pending'), ('A', 'Accepted'), ('R', 'Rejected')], default='P', help_text="User's response to the invitation to present their proposal.", max_length=1)), + ('created_date', models.DateTimeField(default=django.utils.timezone.now)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('event_year', models.ForeignKey(default='', help_text='The event year this proposal is for', on_delete=django.db.models.deletion.CASCADE, related_name='proposals', to='home.eventyear')), + ('speakers', models.ManyToManyField(blank=True, related_name='speaking_proposals', to=settings.AUTH_USER_MODEL)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='proposals', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Document', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, default='', help_text='Brief name/title of the document.', max_length=255)), + ('document', models.FileField(default='', help_text='Upload the document file here.', upload_to='documents/')), + ('uploaded_at', models.DateTimeField(auto_now_add=True)), + ('document_type', models.CharField(choices=[('Slide', 'Slide'), ('Handout', 'Handout'), ('Other', 'Other')], default='Slide', help_text='Type of document (e.g., Slide, Handout).', max_length=50)), + ('proposal', models.ForeignKey(default=1, help_text='Associated proposal for which this document is uploaded.', on_delete=django.db.models.deletion.CASCADE, related_name='documents', to='talks.proposal')), + ], + ), + migrations.CreateModel( + name='Proposing_talk', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Speak at PyCon Africa', max_length=250)), + ('content', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - Content.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('event_year', models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='proposing_talks', to='home.eventyear')), + ('user', models.ForeignKey(default=django.contrib.auth.models.User, on_delete=django.db.models.deletion.CASCADE, related_name='proposing_talk', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Recording', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Recording GL at PyCon Africa', max_length=250)), + ('content', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - Content.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('event_year', models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='recordings', to='home.eventyear')), + ('user', models.ForeignKey(default=django.contrib.auth.models.User, on_delete=django.db.models.deletion.CASCADE, related_name='recording', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Reviewer', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='reviewer_profile', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Review', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('comments', models.TextField(blank=True)), + ('completed', models.BooleanField(default=False)), + ('talk', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='talks.proposal')), + ('reviewer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='talks.reviewer')), + ], + ), + migrations.CreateModel( + name='Speak', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Speak at PyCon Africa', max_length=250)), + ('content', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - Content for speaking at PyCon Africa.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('event_year', models.ForeignKey(default='2024', on_delete=django.db.models.deletion.CASCADE, related_name='speaks', to='home.eventyear')), + ('user', models.ForeignKey(default=django.contrib.auth.models.User, on_delete=django.db.models.deletion.CASCADE, related_name='speak', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='SpeakerInvitation', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('status', models.CharField(choices=[('Pending', 'Pending'), ('Accepted', 'Accepted'), ('Rejected', 'Rejected')], default='Pending', max_length=20)), + ('invitation_sent', models.DateTimeField(default=django.utils.timezone.now)), + ('invitee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='talk_invitations', to=settings.AUTH_USER_MODEL)), + ('talk', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='invitations', to='talks.proposal')), + ], + ), + migrations.CreateModel( + name='SubScore', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('speaker_expertise', models.IntegerField(choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)], default=1)), + ('depth_of_topic', models.IntegerField(choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)], default=1)), + ('relevancy', models.IntegerField(choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)], default=1)), + ('value_or_impact', models.IntegerField(choices=[(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)], default=1)), + ('review', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sub_scores', to='talks.review')), + ], + ), + ] diff --git a/pycons-site/talks/migrations/__init__.py b/pycons-site/talks/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/talks/mixins.py b/pycons-site/talks/mixins.py new file mode 100644 index 0000000..0085eac --- /dev/null +++ b/pycons-site/talks/mixins.py @@ -0,0 +1,9 @@ +from django.core.exceptions import PermissionDenied + +class EditOwnTalksMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnTalksMixin, self).get_object(*args, **kwargs) + if obj.user == self.request.user: + return obj + else: + raise PermissionDenied diff --git a/pycons-site/talks/models.py b/pycons-site/talks/models.py new file mode 100644 index 0000000..9c10b9a --- /dev/null +++ b/pycons-site/talks/models.py @@ -0,0 +1,233 @@ +from django.db import models +from django.urls import reverse +from django.conf import settings +from django.contrib.auth.models import User +from markdownx.models import MarkdownxField +from django.utils import timezone +from embed_video.fields import EmbedVideoField +from django.core.exceptions import ValidationError +from hashids import Hashids +from django.conf import settings +from hashid_field import HashidAutoField +from hashid_field import HashidField +from home.models import EventYear + + +class CFPSubmissionPeriod(models.Model): + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='submission_periods', help_text="The event year this submission period is for") + start_date = models.DateTimeField(help_text="Date and time when proposal submissions start") + end_date = models.DateTimeField(help_text="Date and time when proposal submissions end") + + def __str__(self): + return f"Submission Period for {self.event_year.year}" + + def is_active(self): + """Check if the submission period is currently active.""" + now = timezone.now() + return self.start_date <= now <= self.end_date + + +class Speak(models.Model): + title = models.CharField(max_length=250, null=False, blank=False, help_text='Speak at PyCon Africa') + content = MarkdownxField(default='', help_text = "[Supports Markdown] - Content for speaking at PyCon Africa.", null=False, blank=False + ) + user = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='speak',default=User) + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='speaks') + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("speak") + + + +class Proposing_talk(models.Model): + title = models.CharField(max_length=250, null=False, blank=False, help_text='Speak at PyCon Africa') + content = MarkdownxField(default='', help_text = "[Supports Markdown] - Content.", null=False, blank=False + ) + user = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='proposing_talk',default=User) + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='proposing_talks') + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("proposing_talk") + + + +class Recording(models.Model): + title = models.CharField(max_length=250, null=False, blank=False, help_text='Recording GL at PyCon Africa') + content = MarkdownxField(default='', help_text = "[Supports Markdown] - Content.", null=False, blank=False + ) + user = models.ForeignKey(User, on_delete=models.CASCADE, + related_name='recording',default=User) + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='recordings') + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("recording") + + +class Proposal(models.Model): + TALK_TYPES = ( + ('Lightning Talk', "Lightning Talk - 5 mins"), + ('Short Talk', "Short Talk - 30 mins"), + ('Long Talk', "Long Talk - 45 mins"), + ('Tutorial', "Tutorial - 2 hours"), + ('Sponsored Talk', "Sponsored Talk"), + ('Keynote Speaker', "Keynote Speaker"), + ) + + TALK_CATEGORY = ( + ('GP / Web', "General Python, Web/DevOps"), + ('GC', "General Community"), + ('ET', "Emerging Technologies"), + ('Education', "Education"), + ('O', "Other"), + ) + + STATUS = ( + ('S', 'Submitted'), + ('A', 'Accepted'), + ('W', 'Waiting List'), + ('R', 'Rejected'), + ('RS', 'Rejected by Speaker'), + ) + + PROGRAMMING_EXPERIENCE = ( + ('BGN-L', 'Beginner Level'), + ('INT-L', 'Intermediate Level'), + ('EXP-L', 'Expert Level'), + ('GEN-L', 'General'), + ) + + USER_RESPONSE_CHOICES = [ + ('P', 'Pending'), + ('A', 'Accepted'), + ('R', 'Rejected'), + ] + + title = models.CharField(max_length=1024, help_text="Public title. What topic/project is it all about?") + talk_type = models.CharField(max_length=50, choices=TALK_TYPES) + talk_category = models.CharField(max_length=50, choices=TALK_CATEGORY) + proposal_id = HashidAutoField(primary_key=True, salt=f"talks_proposal{settings.HASHID_FIELD_SALT}", default=None) + + elevator_pitch = MarkdownxField(blank=True, null=True, help_text="[Supports Markdown] - Describe your Talk to your targeted audience.") + talk_abstract = MarkdownxField(blank=True, null=True, help_text="[Supports Markdown] - Your talk_abstract.") + user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="proposals", on_delete=models.CASCADE) + status = models.CharField(max_length=2, choices=STATUS, default='S') + intended_audience = models.CharField(max_length=50, choices=PROGRAMMING_EXPERIENCE, blank=True, null=True) + link_to_preview_video_url = models.URLField(blank=True, help_text='Link to Preview video on your Youtube or Google drive') + anything_else_you_want_to_tell_us = MarkdownxField(blank=True, null=True, help_text="Kindly add anything else you want to tell us?") + special_requirements = MarkdownxField(blank=True, null=True, help_text="If you have any special requirements such as needing travel assistance, accessibility needs, or anything else please let us know here so that we may plan accordingly. (This is not made public nor will the review committee have access to view it.)") + recording_release = models.BooleanField(default=True, help_text="By submitting your talk proposal, you agree to give permission to the conference organizers to record, edit, and release audio and/or video of your presentation. If you do not agree to this, please uncheck this box.") + youtube_video_url = models.URLField(blank=True, help_text='Link to Talk on youtube Video') + youtube_iframe_url = models.URLField(max_length=300, blank=True, help_text='Link to Youtube Iframe') + speakers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='speaking_proposals', blank=True) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="", related_name='proposals', help_text="The event year this proposal is for") + user_response = models.CharField( + max_length=1, + choices=USER_RESPONSE_CHOICES, + default='P', + help_text="User's response to the invitation to present their proposal." + ) + created_date = models.DateTimeField(default=timezone.now) + date_updated = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse("talk_list") + + def average_review_score(self): + reviews = self.reviews.all() + if not reviews.exists(): + return 0 # or handle the case where there are no reviews + total_score = sum(review.average_score() for review in reviews) + return total_score / reviews.count() + + +class SpeakerInvitation(models.Model): + STATUS_CHOICES = ( + ('Pending', 'Pending'), + ('Accepted', 'Accepted'), + ('Rejected', 'Rejected'), + ) + talk = models.ForeignKey('Proposal', related_name='invitations', on_delete=models.CASCADE) + invitee = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='talk_invitations', on_delete=models.CASCADE) + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='Pending') + invitation_sent = models.DateTimeField(default=timezone.now) + + def __str__(self): + return f"Invitation to {self.invitee.email} for {self.talk.title}" + + +class Reviewer(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='reviewer_profile') + + +class SubScore(models.Model): + review = models.ForeignKey('Review', on_delete=models.CASCADE, related_name='sub_scores') + speaker_expertise = models.IntegerField(choices=[(i, i) for i in range(1, 6)], default=1) + depth_of_topic = models.IntegerField(choices=[(i, i) for i in range(1, 6)], default=1) + relevancy = models.IntegerField(choices=[(i, i) for i in range(1, 6)], default=1) + value_or_impact = models.IntegerField(choices=[(i, i) for i in range(1, 6)], default=1) + + def __str__(self): + return f"SubScores for {self.review}" + + def average_score(self): + total = self.speaker_expertise + self.depth_of_topic + self.relevancy + self.value_or_impact + return total / 4 + + + +class Review(models.Model): + talk = models.ForeignKey(Proposal, on_delete=models.CASCADE, related_name='reviews') + reviewer = models.ForeignKey(Reviewer, on_delete=models.CASCADE, related_name='reviews') + comments = models.TextField(blank=True) + completed = models.BooleanField(default=False) + + def __str__(self): + return f"Review by {self.reviewer.user.username} for {self.talk.title}" + + def average_score(self): + sub_scores = self.sub_scores.all() + if not sub_scores.exists(): + return 0 # or handle the case where there are no sub-scores + total_score = sum(sub_score.average_score() for sub_score in sub_scores) + return total_score / sub_scores.count() + + + +class Document(models.Model): + DOCUMENT_TYPES = ( + ('Slide', 'Slide'), + ('Handout', 'Handout'), + ('Other', 'Other'), + ) + + name = models.CharField(max_length=255, blank=True, default='', help_text="Brief name/title of the document.") + document = models.FileField(upload_to='documents/', default='', help_text="Upload the document file here.") + uploaded_at = models.DateTimeField(auto_now_add=True) + proposal = models.ForeignKey(Proposal, default=1, on_delete=models.CASCADE, related_name='documents', help_text="Associated proposal for which this document is uploaded.") + document_type = models.CharField(max_length=50, choices=DOCUMENT_TYPES, default='Slide', help_text="Type of document (e.g., Slide, Handout).") + + def __str__(self): + return f"{self.get_document_type_display()} for {self.proposal.title}" + + def get_absolute_url(self): + return reverse("document_detail", kwargs={"pk": self.pk}) diff --git a/pycons-site/talks/resources.py b/pycons-site/talks/resources.py new file mode 100644 index 0000000..bb63eb3 --- /dev/null +++ b/pycons-site/talks/resources.py @@ -0,0 +1,8 @@ +from import_export import resources +from .models import Proposal + +class ProposalResource(resources.ModelResource): + class Meta: + model = Proposal + fields = ('created_date', 'title', 'talk_type', 'talk_category', 'elevator_pitch', 'talk_abstract', 'link_to_preview_video_url', 'anything_else_you_want_to_tell_us') + export_order = ('created_date', 'title', 'talk_type', 'talk_category', 'elevator_pitch', 'talk_abstract', 'link_to_preview_video_url', 'anything_else_you_want_to_tell_us') \ No newline at end of file diff --git a/pycons-site/talks/serializers.py b/pycons-site/talks/serializers.py new file mode 100644 index 0000000..0926675 --- /dev/null +++ b/pycons-site/talks/serializers.py @@ -0,0 +1,9 @@ +from rest_framework import serializers + +from .models import Proposal + + +class TalkSerializer(serializers.ModelSerializer): + class Meta: + model = Proposal + exclude = ('anything_else_you_want_to_tell_us', 'status') diff --git a/pycons-site/talks/signals.py b/pycons-site/talks/signals.py new file mode 100644 index 0000000..2346c41 --- /dev/null +++ b/pycons-site/talks/signals.py @@ -0,0 +1,74 @@ +from django.dispatch import receiver +from django.core.mail import EmailMultiAlternatives +from django.template.loader import render_to_string +from django.utils.html import strip_tags +from .models import Proposal +from django.db.models.signals import pre_save +from registration.models import Profile +from django.urls import reverse +from django.contrib.sites.models import Site + + + +@receiver(pre_save, sender=Proposal) +def send_status_change_email(sender, instance, **kwargs): + if instance.pk: + try: + original = sender.objects.get(pk=instance.pk) + if original.status != instance.status: + subject_templates = { + 'A': 'PyCon Africa - Congratulations, Your Proposal Has Been Accepted', + 'W': 'PyCon Africa - Your Proposal Is On The Waiting List', + 'R': 'PyCon Africa - Your Proposal Has Been Rejected', + 'S': 'PyCon Africa - Your Proposal Has Been Submitted' + } + template_names = { + 'A': 'emails/talks/proposal_accepted.html', + 'W': 'emails/talks/proposal_waiting.html', + 'R': 'emails/talks/proposal_rejected.html', + 'S': 'emails/talks/proposal_submitted.html' + } + subject = subject_templates.get(instance.status, 'PyCon Africa - Your Proposal Status Update') + html_template = template_names.get(instance.status, 'emails/talks/proposal_status_changed.html') + + # Retrieve the user's profile to get the full name + try: + user_profile = Profile.objects.get(user=instance.user) + full_name = user_profile.get_full_name() + except Profile.DoesNotExist: + full_name = instance.user.username # Fallback to username if profile doesn't exist + + # Get the current site + site = Site.objects.get_current() + domain = site.domain + + # Generate the URL to the talk detail page + talk_url = f"https://{domain}{reverse('talks:talk_details', kwargs={'year': instance.event_year.year, 'pk': instance.proposal_id.hashid})}" + + # Generate the URL to accept or reject the invitation + response_url = f"https://{domain}{reverse('talks:respond_to_invitation', kwargs={'year': instance.event_year.year, 'pk': instance.proposal_id.hashid})}" + + + html_content = render_to_string(html_template, { + 'proposal': instance, + 'user': instance.user, + 'full_name': full_name, + 'talk_url': talk_url, + 'response_url': response_url + }) + text_content = strip_tags(html_content) + + from_email = 'PyCon Africa Program\'s Team ' + + email = EmailMultiAlternatives( + subject, + text_content, + from_email, # Use the formatted sender's name and email + [instance.user.email] + ) + email.attach_alternative(html_content, "text/html") + email.send() + except sender.DoesNotExist: + pass # Handle the case where the Proposal does not exist when the email is triggered + + \ No newline at end of file diff --git a/pycons-site/talks/templatetags/__init__.py b/pycons-site/talks/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/talks/templatetags/markdown_extras.py b/pycons-site/talks/templatetags/markdown_extras.py new file mode 100644 index 0000000..ee1ea4c --- /dev/null +++ b/pycons-site/talks/templatetags/markdown_extras.py @@ -0,0 +1,12 @@ +from django import template +from django.template.defaultfilters import stringfilter + +import markdown as md + +register = template.Library() + + +@register.filter() +@stringfilter +def markdown(value): + return md.markdown(value, extensions=['markdown.extensions.fenced_code']) diff --git a/pycons-site/talks/templatetags/talks_tags.py b/pycons-site/talks/templatetags/talks_tags.py new file mode 100644 index 0000000..0b8c0ae --- /dev/null +++ b/pycons-site/talks/templatetags/talks_tags.py @@ -0,0 +1,39 @@ +from django import template +from django.utils import timezone +from talks.models import * +from home.models import EventYear + +register = template.Library() + +@register.inclusion_tag('2024/talks/user_talks_summary.html') +def user_talks_summary(user): + # Attempt to find the current or next CFP period + current_year = timezone.now().year + event_year = EventYear.objects.filter(year=current_year).first() + submission_period = None + if event_year: + submission_periods = CFPSubmissionPeriod.objects.filter(event_year=event_year).order_by('start_date') + submission_period = next((period for period in submission_periods if period.start_date <= timezone.now() <= period.end_date), None) + if not submission_period: + submission_period = next((period for period in submission_periods if period.start_date > timezone.now()), None) + + # Fetch the first 2 or 3 submitted talks for the user + submitted_talks = Proposal.objects.filter(user=user).order_by('-created_date')[:3] + invited_talks = Proposal.objects.filter(speakers=user).distinct().order_by('-created_date')[:3] + + + return { + 'submitted_talks': submitted_talks, + 'invited_talks': invited_talks, + 'active_period': submission_period is not None and submission_period.start_date <= timezone.now() <= submission_period.end_date, + 'upcoming_period': submission_period and submission_period.start_date > timezone.now(), + 'event_year': event_year + } + + + +@register.inclusion_tag('2024/talks/invitation_list.html', takes_context=True) +def invitation_list(context): + request = context['request'] + invitations = SpeakerInvitation.objects.filter(invitee=request.user, status='Pending') + return {'invitations': invitations} \ No newline at end of file diff --git a/pycons-site/talks/tests.py b/pycons-site/talks/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/talks/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/talks/urls.py b/pycons-site/talks/urls.py new file mode 100644 index 0000000..1b614b5 --- /dev/null +++ b/pycons-site/talks/urls.py @@ -0,0 +1,69 @@ +from rest_framework import routers +from django.urls import re_path +from django.urls import path +from talks import views +from .views import * +from django.conf.urls.static import static +from django.conf import settings + +app_name = 'talks' +router = routers.DefaultRouter() +router.register(r'talks', TalkViewsSets) + +urlpatterns = [ + # For listing all accepted talks (not year-specific) + path('accepted_talks/', login_required(views.AcceptedTalksView.as_view()), name='accepted_talks'), + + # Submission for a specific year + path('submit_talk/', login_required(views.submit_talk), name='submit_talk'), + + # Editing a talk, assuming pk is sufficient for identifying the talk + path('/edit_talk/', login_required(views.edit_talk), name='edit_talk'), + # For listing talks submitted by the logged-in user (not year-specific) + path('talk_list/', login_required(views.TalkList.as_view()), name='talk_list'), + + + # Detail views for talks/talks//talk_details/ + path('/talk_details/', login_required(views.TalkDetailView.as_view()), name='talk_details'), + re_path(r'^(?P[\w-]+)/detail/$', (views.TalksDetailView.as_view()), name='talk_detail'), + + path('/talks/success/', SuccessView.as_view(), name='talks_success'), + + # Updated paths to include the year + path('/speaking/', views.speaking, name='speaking'), + path('/proposing_a_talk/', views.proposing, name='proposing'), + path('/recording/', views.recording, name='recording'), + + # Redirects for accessing without specifying a year + path('speaking/', views.speaking, name='speaking_current_year'), + path('recording/', views.recording, name='recording_current_year'), + path('proposing_a_talk/', views.proposing, name='proposing_current_year'), + path('proposal//respond/', respond_to_invitation, name='respond_to_invitation'), + + + path('submitted', login_required(views.SuccessView.as_view()), name='submitted'), + path('uploads', views.home, name='home'), + path('uploads_simple', views.simple_upload, name='simple_upload'), + path('uploads_form', views.model_form_upload, name='model_form_upload'), + + #Added Speaker(s) invitation + path('/invite-speaker/', views.send_speaker_invitation, name='send_speaker_invitation'), + path('/accept/', views.accept_invitation, name='accept_invitation'), + path('/reject/', views.reject_invitation, name='reject_invitation'), + + #Proposal Review + path('reviews/', views.list_talks_to_review, name='talks_to_review'), + path('review//', views.review_talk, name='review_talk'), + path('review_talk/success/', views.review_success, name='review_success'), + path('reviewed-by-category/', reviewed_talks_by_category, name='reviewed_talks_by_category'), + path('reviewed_by_type/', views.reviewed_talks_by_type, name='reviewed_talks_by_type'), + + + + ] + +urlpatterns += router.urls +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + diff --git a/pycons-site/talks/views.py b/pycons-site/talks/views.py new file mode 100644 index 0000000..980dabd --- /dev/null +++ b/pycons-site/talks/views.py @@ -0,0 +1,784 @@ +from django.shortcuts import get_object_or_404 +from django.urls import reverse_lazy +from django.core.mail import send_mail + +from django.core.files.storage import FileSystemStorage +from django.shortcuts import render, redirect, HttpResponse + +from datetime import datetime + +from rest_framework import viewsets + +from .serializers import TalkSerializer +from .forms import * +from .forms import ProposalForm +import logging +from django.views.generic.detail import DetailView +from django.views.generic import TemplateView, UpdateView +from django.contrib.auth.decorators import login_required, permission_required +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +from django.contrib import messages +#Sending html emails to user +from django.core.mail import EmailMultiAlternatives +from django.template.loader import render_to_string +from django.utils.html import strip_tags +from django.http import Http404 +from .resources import ProposalResource +from home.models import EventYear +from registration.models import Profile +from django.db.models import Avg, F, Count +from django.contrib.sites.models import Site +from django.utils.text import Truncator + + +logger = logging.getLogger(__name__) + +@login_required +def submit_talk(request, year): + # Check if the user has a profile + try: + profile = Profile.objects.get(user=request.user) + except Profile.DoesNotExist: + return redirect(reverse('profiles:create_profile')) + + try: + event_year = EventYear.objects.get(year=year) + submission_periods = CFPSubmissionPeriod.objects.filter(event_year=event_year).order_by('start_date') + active_period = None + upcoming_period = None + + for period in submission_periods: + if period.start_date <= timezone.now() <= period.end_date: + active_period = period + break + elif timezone.now() < period.start_date and not upcoming_period: + upcoming_period = period + + context = { + 'title': 'Submit a Talk', + 'year': year, + 'active_period': active_period, + 'upcoming_period': upcoming_period, + 'is_sponsor_or_keynote': profile.is_a_sponsor_or_keynote_speaker, + } + + if request.method == "POST": + if active_period or profile.is_a_sponsor_or_keynote_speaker: + form = ProposalForm(request.POST, user=request.user) + if form.is_valid(): + proposal = form.save(commit=False) + proposal.user = request.user + proposal.event_year = event_year + proposal.save() + + # Send confirmation email + subject = 'Talk Submission Confirmation' + html_content = render_to_string('emails/talks/submission_confirmation.html', {'user': request.user, 'proposal': proposal}) + text_content = strip_tags(html_content) + email = EmailMultiAlternatives(subject, text_content, to=[request.user.email]) + email.attach_alternative(html_content, "text/html") + email.send() + + return redirect('talks:submitted', year=event_year.year) + else: + logger.debug(f"Form errors: {form.errors}") + context['form'] = form + else: + context['form'] = ProposalForm(user=request.user) + else: + context['form'] = ProposalForm(user=request.user) + logger.debug("Form added to context for GET request.") + + except EventYear.DoesNotExist: + return redirect(reverse_lazy('talks:no_event_year_error')) + + template_path = f"{year}/talks/talk_form.html" + logger.debug(f"Rendering template: {template_path} with context: {context}") + return render(request, template_path, context) + + + + +@login_required +def edit_talk(request, year, pk): + proposal = get_object_or_404(Proposal, pk=pk) + if str(proposal.event_year.year) != str(year): + raise Http404("Proposal does not exist for the given year.") + + if request.method == "POST": + form = ProposalForm(request.POST, instance=proposal) + if form.is_valid(): + form.save() + return redirect('talks:talk_list', year=proposal.event_year.year) + else: + form = ProposalForm(instance=proposal) + + template_prefix = f"{proposal.event_year.year}/talks/" + context = { + 'form': form, + 'year': year, + 'proposal': proposal, # Add the proposal to the context + } + return render(request, template_prefix + 'edit_talk.html', context) + + + + +class TalkList(TemplateView): + def get_context_data(self, **kwargs): + context = super(TalkList, self).get_context_data(**kwargs) + year = self.kwargs.get('year', timezone.now().year) + context['year'] = year + + event_year = get_object_or_404(EventYear, year=year) + submission_periods = CFPSubmissionPeriod.objects.filter(event_year=event_year).order_by('start_date') + + active_period = None + for period in submission_periods: + if period.start_date <= timezone.now() <= period.end_date: + active_period = period + break + + # Get the user's profile to check if they are a sponsor or keynote speaker + profile = Profile.objects.get(user=self.request.user) + + context.update({ + 'submitted_talks': Proposal.objects.filter(user=self.request.user, event_year__year=year), + 'submission_periods': submission_periods, + 'active_period': active_period, + 'is_editable': active_period is not None or profile.is_a_sponsor_or_keynote_speaker, + 'is_sponsor_or_keynote': profile.is_a_sponsor_or_keynote_speaker + }) + + return context + + def get_template_names(self): + year = self.kwargs.get('year', timezone.now().year) + template_path = f"{year}/talks/talk_list.html" + return [template_path] + + + +class TalkView(UpdateView): + form_class = ProposalForm + model = Proposal + slug_field = 'slug' + + def get_success_url(self): + proposal = self.get_object() + return reverse_lazy('talks:submitted', kwargs={'year': proposal.event_year.year}) + + def get_template_names(self): + proposal = self.get_object() + return [f"{proposal.event_year.year}/talks/talk.html"] + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + proposal = self.get_object() + context.update({ + 'title': "Talk Details", + 'year': proposal.event_year.year, + 'speakers': proposal.speakers.all() # Include speakers in the context + }) + return context + + + + +class TalkDetailView(TemplateView): + def get_template_names(self): + proposal = get_object_or_404(Proposal, proposal_id=self.kwargs.get('pk')) + event_year = proposal.event_year.year + return [f"{event_year}/talks/talk_details.html"] + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + proposal = get_object_or_404(Proposal, proposal_id=self.kwargs.get('pk')) + submission_periods = CFPSubmissionPeriod.objects.filter(event_year=proposal.event_year).order_by('start_date') + + active_period = None + for period in submission_periods: + if period.start_date <= timezone.now() <= period.end_date: + active_period = period + break + + # Determine if the user can upload documents + can_upload = proposal.status == 'A' + is_primary_speaker = self.request.user == proposal.user + is_invited_speaker = self.request.user in proposal.speakers.all() + can_upload = can_upload and (is_primary_speaker or is_invited_speaker) + + # Check if a slide has already been uploaded + uploaded_documents = Document.objects.filter(proposal=proposal, document_type='Slide') + has_uploaded_slide = uploaded_documents.exists() + latest_slide = uploaded_documents.latest('uploaded_at') if has_uploaded_slide else None + + # Add the form to the context + context.update({ + 'title': "Accepted Talks", + 'year': proposal.event_year.year, + 'talk': proposal, + 'speakers': proposal.speakers.all(), + 'submission_periods': submission_periods, + 'active_period': active_period, + 'is_editable': active_period is not None, + 'can_upload': can_upload, + 'has_uploaded_slide': has_uploaded_slide, + 'latest_slide': latest_slide, + 'form': DocumentForm(proposal=proposal), # Pass the proposal instance + }) + return context + + def post(self, request, *args, **kwargs): + proposal = get_object_or_404(Proposal, proposal_id=self.kwargs.get('pk')) + form = DocumentForm(request.POST, request.FILES, proposal=proposal) + if form.is_valid(): + form.save() + messages.success(request, 'Your slides have been uploaded successfully.') + return redirect(reverse('talks:talk_details', kwargs={'year': proposal.event_year.year, 'pk': proposal.proposal_id.hashid})) + # If the form is not valid, re-render the page with form errors + context = self.get_context_data(form=form) + return self.render_to_response(context) + +class TalksDetailView(DetailView): + model = Proposal + context_object_name = 'talk' + slug_field = 'proposal_id' + slug_url_kwarg = 'proposal_id' + + def get_template_names(self): + proposal = self.get_object() + return [f"{proposal.event_year.year}/schedule/talk_details.html"] + + def get_context_data(self, **kwargs): + context = super(TalksDetailView, self).get_context_data(**kwargs) + proposal = self.get_object() + + # Fetch the Profile objects for all speakers (main and additional) + speakers = [proposal.user] + list(proposal.speakers.all()) + speaker_profiles = Profile.objects.filter(user__in=speakers) + + # Generate the meta tags dynamically + meta_title = f"{proposal.title} | PyCon Africa {proposal.event_year.year}" + meta_description = Truncator(proposal.talk_abstract).words(30, truncate='...') if proposal.talk_abstract else "Join us at PyCon Africa for an insightful talk." + meta_og_image = speaker_profiles.first().profile_image.url if speaker_profiles.exists() and speaker_profiles.first().profile_image else 'https://res.cloudinary.com/pycon-africa/image/upload/v1722977619/website_storage_location/media/pyconafrica.png' + + context.update({ + 'title': "Talk Details", + 'year': proposal.event_year.year, + 'related_talks': Proposal.objects.filter(status='A', event_year=proposal.event_year).order_by('?')[:5], + 'speakers': speaker_profiles, # Pass Profile objects instead of users + 'meta_title': meta_title, + 'meta_description': meta_description, + 'meta_og_image': meta_og_image, + }) + return context + + + + + + + + +class SuccessView(TemplateView): + def get_template_names(self): + # Attempt to fetch the event year from URL kwargs + year = self.kwargs.get('year', timezone.now().year) + # Verify if the EventYear exists, raise 404 if not + if not EventYear.objects.filter(year=year).exists(): + raise Http404("Event year does not exist.") + # Construct the template path using the event year + return [f"{year}/talks/success.html"] + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + # Assuming 'year' is passed as a URL parameter, otherwise default to the current year + year = self.kwargs.get('year', timezone.now().year) + context['title'] = 'Talk Submission Successful' + context['year'] = year + return context + + +class TalkViewsSets(viewsets.ReadOnlyModelViewSet): + serializer_class = TalkSerializer + queryset = Proposal.objects.all() + + +class AcceptedTalksView(TemplateView): + template_name = "talks/accepted_talks.html" + + def get_context_data(self, **kwargs): + context = super(AcceptedTalksView, self).get_context_data(**kwargs) + context['title'] = "Accepted Talks" + context['year'] = datetime.now().year + talks_list = Proposal.objects.filter(status='A').select_related('user') + + paginator = Paginator(talks_list, 10) # Show 10 posts per page + page = self.request.GET.get('page') + try: + accepted_talks = paginator.page(page) + except PageNotAnInteger: + # If page is not an integer, deliver first page. + accepted_talks = paginator.page(1) + except EmptyPage: + # If page is out of range (e.g. 9999), deliver last page of results. + accepted_talks = paginator.page(paginator.num_pages) + context['accepted_talks'] = accepted_talks + return context + + + +def export(request): + proposal_resource = ProposalResource() + dataset = proposal_resource.export() + response = HttpResponse(dataset.csv, content_type='text/csv') + response['Content-Disposition'] = 'attachment; filename="proposals.csv"' + return response + + + +def model_form_upload(request): + if request.method == 'POST': + form = DocumentForm(request.POST, request.FILES) + if form.is_valid(): + form.save() + return redirect('home') + else: + form = DocumentForm() + return render(request, 'model_form_upload.html', { + 'form': form + }) + + + +def home(request): + documents = Document.objects.all() + return render(request, 'upload/home.html', { 'documents': documents }) + +def redirect_to_current_year_speaking(request): + current_year = timezone.now().year + return redirect('speaking', year=current_year) + +def redirect_to_current_year_proposing(request): + current_year = timezone.now().year + return redirect('proposing', year=current_year) + +def redirect_to_current_year_recording(request): + current_year = timezone.now().year + return redirect('recording', year=current_year) + +def speaking(request, year=None): + year = year or timezone.now().year + event_year = get_object_or_404(EventYear, year=year) + speaks = Speak.objects.filter(event_year=event_year).order_by('-date_created') + template_name = f'{year}/talks/talks.html' + return render(request, template_name, {'speaks': speaks, 'event_year': event_year}) + +def recording(request, year=None): + year = year or timezone.now().year + event_year = get_object_or_404(EventYear, year=year) + recordings = Recording.objects.filter(event_year=event_year).order_by('-date_created') + template_name = f'{year}/talks/recordings.html' + return render(request, template_name, {'recordings': recordings, 'event_year': event_year}) + +def proposing(request, year=None): + year = year or timezone.now().year + event_year = get_object_or_404(EventYear, year=year) + proposing_talks = Proposing_talk.objects.filter(event_year=event_year).order_by('-date_created') + template_name = f'{year}/talks/proposing_a_talk.html' + return render(request, template_name, {'proposing_talks': proposing_talks, 'event_year': event_year}) + + +@login_required +def send_speaker_invitation(request, year, pk): + if request.method == 'POST': + user_email = request.POST.get('user_email') + event_year = get_object_or_404(EventYear, year=year) + proposal = get_object_or_404(Proposal, pk=pk, event_year=event_year) + user = get_object_or_404(User, email=user_email) + sender_name = request.user.get_full_name() or request.user.username + invitation, created = SpeakerInvitation.objects.get_or_create(talk=proposal, invitee=user) + + if created: + email_body = f"Dear Speaker,\n\nYou have been invited by {sender_name} to join a session titled '{proposal.title}' during PyCon Africa {event_year.year}. \n\nPlease visit our site (https://africa.pycon.org/) to respond to this invitation.\n\nBest,\nPyCon Africa's Team" + send_mail( + f'Invitation to Speak at PyCon Africa - {proposal.title}', + email_body, + 'noreply@pycon.africa', + [user_email], + fail_silently=False, + ) + return redirect('talks:talk_details', year=year, pk=pk) + else: + # Handle the case for GET request or show an error message + return render(request, '2024/talks/speaker_invite_error.html', {'error': 'This action requires a POST request.'}) + + +@login_required +def accept_invitation(request, year, pk): + proposal = get_object_or_404(Proposal, pk=pk) + invitation = get_object_or_404(SpeakerInvitation, talk=proposal, invitee=request.user) + if invitation.status == 'Pending': + invitation.status = 'Accepted' + invitation.save() + proposal.speakers.add(request.user) + return redirect('profiles:profile_home') + +@login_required +def reject_invitation(request, year, pk): + proposal = get_object_or_404(Proposal, pk=pk) + invitation = get_object_or_404(SpeakerInvitation, talk=proposal, invitee=request.user) + if invitation.status == 'Pending': + invitation.status = 'Rejected' + invitation.save() + return redirect('profiles:profile_home') + + + + + +@login_required +@permission_required('talks.view_talk', raise_exception=True) +def list_talks_to_review(request, year): + try: + event_year = EventYear.objects.get(year=year) + except EventYear.DoesNotExist: + raise Http404("Event year does not exist.") + + try: + reviewer = Reviewer.objects.get(user=request.user) + # Fetch all reviews by this reviewer for talks in this event year + reviewed_talk_ids = Review.objects.filter(reviewer=reviewer, talk__event_year=event_year).values_list('talk__proposal_id', flat=True) + # Filter out talks that have been reviewed by this reviewer + talks_awaiting_review = Proposal.objects.filter(event_year=event_year, status='S').exclude(proposal_id__in=reviewed_talk_ids).order_by('talk_type') + + # Group talks by talk type + talks_by_type = {} + for talk in talks_awaiting_review: + if talk.talk_type not in talks_by_type: + talks_by_type[talk.talk_type] = [] + talks_by_type[talk.talk_type].append(talk) + + # Fetch reviewed talks with their scores + talks_reviewed_with_scores = [] + for talk_id in reviewed_talk_ids: + talk = Proposal.objects.get(proposal_id=talk_id) + avg_score_dict = Review.objects.filter(talk=talk).aggregate( + avg_speaker_expertise=Avg('sub_scores__speaker_expertise'), + avg_depth_of_topic=Avg('sub_scores__depth_of_topic'), + avg_relevancy=Avg('sub_scores__relevancy'), + avg_value_or_impact=Avg('sub_scores__value_or_impact') + ) + avg_speaker_expertise = avg_score_dict['avg_speaker_expertise'] or 0 + avg_depth_of_topic = avg_score_dict['avg_depth_of_topic'] or 0 + avg_relevancy = avg_score_dict['avg_relevancy'] or 0 + avg_value_or_impact = avg_score_dict['avg_value_or_impact'] or 0 + avg_score = (avg_speaker_expertise + avg_depth_of_topic + avg_relevancy + avg_value_or_impact) / 4 + talks_reviewed_with_scores.append((talk, avg_score)) + + context = { + 'talks_by_type': talks_by_type, + 'talks_reviewed_with_scores': talks_reviewed_with_scores, + 'year': year + } + + except Reviewer.DoesNotExist: + logger.error(f"Reviewer does not exist for user {request.user}") + messages.error(request, "You don't yet have rights to review proposals, contact the admin to give you the rights") + context = { + 'year': year, + 'no_reviewer_rights': True + } + + return render(request, '2024/talks/reviews/talk_list.html', context) + +@login_required +@permission_required('reviews.add_review', raise_exception=True) +def review_talk(request, year, pk): + event_year = get_object_or_404(EventYear, year=year) + talk = get_object_or_404(Proposal, pk=pk, event_year=event_year) + + try: + reviewer = Reviewer.objects.get(user=request.user) + except Reviewer.DoesNotExist: + return HttpResponse("You are not registered as a reviewer.", status=401) + + already_reviewed = Review.objects.filter(talk=talk, reviewer=reviewer).exists() + + if request.method == 'POST' and not already_reviewed: + form = ReviewForm(request.POST) + if form.is_valid(): + review = form.save(commit=False) + review.talk = talk + review.reviewer = reviewer + review.save() + # Save sub-scores + sub_score = SubScore( + review=review, + speaker_expertise=form.cleaned_data['speaker_expertise'], + depth_of_topic=form.cleaned_data['depth_of_topic'], + relevancy=form.cleaned_data['relevancy'], + value_or_impact=form.cleaned_data['value_or_impact'] + ) + sub_score.save() + return redirect(reverse('talks:review_success', kwargs={'year': year})) + else: + form = ReviewForm() + + return render(request, '2024/talks/reviews/talk_review.html', { + 'form': form, + 'talk': talk, + 'year': year, + 'already_reviewed': already_reviewed, + }) + +@login_required +def review_success(request, year): + try: + event_year = EventYear.objects.get(year=year) + return render(request, '2024/talks/reviews/review_success.html', {'year': year}) + except EventYear.DoesNotExist: + return HttpResponse("The specified event year does not exist.", status=404) + + + + +@login_required +@permission_required('talks.view_talk', raise_exception=True) +def reviewed_talks_by_category(request, year): + try: + event_year = EventYear.objects.get(year=year) + except EventYear.DoesNotExist: + raise Http404("Event year does not exist.") + + category_talks_scores = [] + for category_code, category_label in Proposal.TALK_CATEGORY: + talks = Proposal.objects.filter( + event_year=event_year, + talk_category=category_code, + reviews__isnull=False + ).annotate( + avg_speaker_expertise=Avg('reviews__sub_scores__speaker_expertise'), + avg_depth_of_topic=Avg('reviews__sub_scores__depth_of_topic'), + avg_relevancy=Avg('reviews__sub_scores__relevancy'), + avg_value_or_impact=Avg('reviews__sub_scores__value_or_impact'), + avg_score=( + F('avg_speaker_expertise') + F('avg_depth_of_topic') + F('avg_relevancy') + F('avg_value_or_impact') + ) / 4, + submission_count=Count('user__proposals', distinct=True) # Ensure distinct count + ).order_by('-avg_score') + + # Adding user's name and surname to each talk + for talk in talks: + user_profile = Profile.objects.get(user=talk.user) + talk.user_name = user_profile.name + talk.user_surname = user_profile.surname + + # Calculate the rank for each talk + for i, talk in enumerate(talks): + talk.rank = i + 1 + + if talks.exists(): + category_talks_scores.append((category_label, talks)) + + return render(request, '2024/talks/reviews/reviewed_talks_by_category.html', { + 'category_talks_scores': category_talks_scores, + 'year': year + }) + + +@login_required +@permission_required('talks.view_talk', raise_exception=True) +def reviewed_talks_by_type(request, year): + try: + event_year = EventYear.objects.get(year=year) + except EventYear.DoesNotExist: + raise Http404("Event year does not exist.") + + type_talks_scores = [] + # Grouping by talk_type and calculating the average score + for talk_type_code, talk_type_label in Proposal.TALK_TYPES: + talks = Proposal.objects.filter( + event_year=event_year, + talk_type=talk_type_code, + reviews__isnull=False + ).annotate( + avg_speaker_expertise=Avg('reviews__sub_scores__speaker_expertise'), + avg_depth_of_topic=Avg('reviews__sub_scores__depth_of_topic'), + avg_relevancy=Avg('reviews__sub_scores__relevancy'), + avg_value_or_impact=Avg('reviews__sub_scores__value_or_impact'), + submission_count=Count('user__proposals', distinct=True) # Ensure distinct count + ).annotate( + avg_score=( + F('avg_speaker_expertise') + F('avg_depth_of_topic') + F('avg_relevancy') + F('avg_value_or_impact') + ) / 4 + ).order_by('-avg_score') + + # Adding user's name and surname to each talk + for talk in talks: + user_profile = Profile.objects.get(user=talk.user) + talk.user_name = user_profile.name + talk.user_surname = user_profile.surname + + if talks.exists(): + # Adding rank to each talk + for rank, talk in enumerate(talks, start=1): + talk.rank = rank + type_talks_scores.append((talk_type_label, talks)) + + return render(request, '2024/talks/reviews/reviewed_talks_by_type.html', { + 'type_talks_scores': type_talks_scores, + 'year': year + }) + +# Class-based +''' +@login_required +@permission_required('reviews.add_review', raise_exception=True) +class TalksToReviewListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): + model = Proposal + template_name = '2024/talks/reviews/talk_list.html' + context_object_name = 'talks' + permission_required = ('talks.view_talk',) + + def get_queryset(self): + # Filter talks that are submitted and pending review + return Proposal.objects.filter(status='Submitted').order_by('-created_date') + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + # Add any additional context if necessary + return context + + +@login_required +@permission_required('reviews.add_review', raise_exception=True) +class ReviewTalkView(UpdateView): + model = Review + form_class = ReviewForm + template_name = '2024/talks/reviews/talk_review.html' + context_object_name = 'review' + + def get_object(self, queryset=None): + talk = get_object_or_404(Proposal, pk=self.kwargs.get('pk')) + review, created = Review.objects.get_or_create(talk=talk, reviewer=self.request.user) + return review + + def form_valid(self, form): + response = super().form_valid(form) + return response + + def get_success_url(self): + return reverse_lazy('reviews:review_list') # Redirect to the list of talks after submitting a review +''' + +@login_required +@permission_required('reviews.add_review', raise_exception=True) +class TalkReviewDetailView(DetailView): + model = Proposal + template_name = '2024/talks/reviews/talk_review_detail.html' + context_object_name = 'talk' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + reviews = self.object.reviews.all() + context['reviews'] = reviews + if reviews.exists(): + context['average_score'] = reviews.aggregate(Avg('score'))['score__avg'] + context['is_accepted'] = context['average_score'] >= 4 # Example criteria (I will have to chnage this) + return context + + + +def simple_upload(request): + if request.method == 'POST' and request.FILES['myfile']: + myfile = request.FILES['myfile'] + fs = FileSystemStorage() + filename = fs.save(myfile.name, myfile) + uploaded_file_url = fs.url(filename) + return render(request, 'upload/simple_upload.html', { + 'uploaded_file_url': uploaded_file_url + }) + return render(request, 'upload/simple_upload.html') + + +def model_form_upload(request): + if request.method == 'POST': + form = DocumentForm(request.POST, request.FILES) + if form.is_valid(): + form.save() + return redirect('home') + else: + form = DocumentForm() + return render(request, 'upload/model_form_upload.html', { + 'form': form + }) + + + + + +@login_required +def respond_to_invitation(request, year, pk): + # Get the event year and proposal based on the provided year and proposal ID + event_year = get_object_or_404(EventYear, year=year) + proposal = get_object_or_404(Proposal, pk=pk, event_year=event_year) + + if request.method == 'POST': + form = ProposalResponseForm(request.POST, instance=proposal) + if form.is_valid(): + # Determine the user's response + user_response = form.cleaned_data.get('user_response', '') + + # Update the proposal's status and user_response based on the user's response + if user_response == 'A': + proposal.user_response = 'A' + proposal.status = 'A' # Set status to 'Accepted' if it wasn't set before + elif user_response == 'R': + proposal.user_response = 'R' + proposal.status = 'RS' # Set status to 'Rejected by Speaker' + + proposal.save() # Save the updated proposal + + # Get the current site domain + site = Site.objects.get_current() + domain = site.domain + + # Generate the URL to the talk detail page + talk_url = f"https://{domain}{reverse('talks:talk_details', kwargs={'year': year, 'pk': proposal.proposal_id.hashid})}" + + # Send appropriate email based on user response + subject = "" + html_template = "" + + if proposal.user_response == 'A': + subject = "Thank You for Accepting to Speak at PyCon Africa" + html_template = 'emails/talks/accepted_response.html' + elif proposal.user_response == 'R': + subject = "Thank You for Your Response" + html_template = 'emails/talks/rejected_response.html' + + html_content = render_to_string(html_template, { + 'proposal': proposal, + 'full_name': Profile.objects.get(user=proposal.user).get_full_name(), + 'talk_url': talk_url, # Include talk_url in context + }) + text_content = strip_tags(html_content) + email = EmailMultiAlternatives( + subject, + text_content, + 'PyCon Africa Program\'s Team ', + [proposal.user.email] + ) + email.attach_alternative(html_content, "text/html") + email.send() + + messages.success(request, 'Your response has been recorded.') + # Redirect to the talk details page with the correct year + return redirect('talks:talk_details', year=event_year.year, pk=proposal.pk) + else: + form = ProposalResponseForm(instance=proposal) + + template_path = f'{event_year.year}/talks/proposal_response_form.html' + return render(request, template_path, {'form': form, 'proposal': proposal}) diff --git a/pycons-site/templates/404.html b/pycons-site/templates/404.html new file mode 100644 index 0000000..d69a6dc --- /dev/null +++ b/pycons-site/templates/404.html @@ -0,0 +1,299 @@ +{% load %} +{% load static %} + + + + + + 404 | PyCon Africa + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + {% block content %} + +
+ +
+ + +
+
+ {% endblock %} + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pycons-site/templates/base.html b/pycons-site/templates/base.html new file mode 100644 index 0000000..2ff1efd --- /dev/null +++ b/pycons-site/templates/base.html @@ -0,0 +1,316 @@ +{% load %} +{% load static %} + + + + + + + {% block meta_title %}{% endblock %}{% if settings.SITE_TITLE %} | {{ settings.SITE_TITLE }}{% endif %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% block extra_meta %}{% endblock %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + {% block content %} + + {% endblock %} + + + {% include '2024/footer.html' %} + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pycons-site/templates/partials/schedule_home.html b/pycons-site/templates/partials/schedule_home.html new file mode 100644 index 0000000..6fa6db8 --- /dev/null +++ b/pycons-site/templates/partials/schedule_home.html @@ -0,0 +1,26 @@ + +
+ {% if day_schedules %} +

Schedule Preview

+ {% for day_schedule in day_schedules %} +

{{ day_schedule.day.conference_day }}

+
    + {% for schedule in day_schedule.schedules %} +
  • + {{ schedule.start_time|time:"H:i" }} - {{ schedule.end_time|time:"H:i" }} +
    + {% if schedule.is_an_event %} + Event: {{ schedule.event }} + {% else %} + Talk: {{ schedule.talk.title }} by + {{ schedule.talk.user.get_full_name }}{% if schedule.talk.speakers.exists %} and others{% endif %} + {% endif %} +
  • + {% endfor %} +
+ {% endfor %} + View Full Schedule + {% else %} +

No schedule available yet.

+ {% endif %} +
diff --git a/pycons-site/templates/profiles/create_profile.html b/pycons-site/templates/profiles/create_profile.html new file mode 100644 index 0000000..a30f7f5 --- /dev/null +++ b/pycons-site/templates/profiles/create_profile.html @@ -0,0 +1,129 @@ +{% extends "base.html" %} +{% load i18n static avatar_tags crispy_forms_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Create Profile || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + + + +
+
+

Create your Profile {% if user_profile.surname %}{{ request.user.user_profile.name }} {% else %}{% firstof user.get_short_name user.get_username|capfirst %}{% endif %}

+ +
    +
  • + Home + / +
  • +
  • + Create Profile +
  • +

+ +
+ +
+ + + +
+
+
+ +
+ + {% include 'profiles/profilepic_side.html' %} + + + +
+
+
+
LAST LOGIN
+
Date: {{ request.user.last_login.date }}
+
Time: {{ request.user.last_login.time }}
+
+
+
+ + + {% if user_profile %} + + + + Overall + 2 + + + + + + Update my Profile + + + + + + Change Password + + + + + + My Submitted Talks + + + + +
+

   Update my Profile

+

   Change Password

+

   My Submitted Talks

+ + + {% else %} + + {% endif %}
+ +
+ + + +
+
+
+ +
+

Enter your profile details below

+
{% csrf_token %} + + {{ form|crispy }} + {{ form.media }} +
+
+ + + +
+
+
+ + +
+
+
+ +
+
+
+ + + +{% endblock %} + + \ No newline at end of file diff --git a/pycons-site/templates/profiles/home.html b/pycons-site/templates/profiles/home.html new file mode 100644 index 0000000..0e80828 --- /dev/null +++ b/pycons-site/templates/profiles/home.html @@ -0,0 +1,297 @@ +{% extends "base.html" %} +{% load i18n tz static avatar_tags crispy_forms_tags markdown_extras countries profile_tags talks_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Profile || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + + +
+
+

Hello {% if user_profile %}{{ request.user.user_profile.name }} {% else %}{% firstof user.get_short_name user.get_username|capfirst %}{% endif %}

+ Welcome to your Profile + +
    +
  • + Home + / +
  • +
  • + Profile +
  • +

+ + +
+ +
+ + + + +
+
+
+
+ +
+ + {% include 'profiles/profilepic_side.html' %} + + + +
+
+
+
LAST LOGIN
+
Date: {{ request.user.last_login.date }}
+
Time: {{ request.user.last_login.time }}
+
+
+
+ + + {% if user_profile %} + + + + My Profile + + + + + + Update my Profile + + + + + + Change Password + + + + + + My Submitted Talks + + + + + + Submit a new Talk + + + + + {% if perms.reviews.add_review %} + + + Review Proposals + + + {% endif %} + + {% if request.user.is_superuser %} + + + Proposals by Category + + + + Proposals by Type + + + {% endif %} + + + {% if perms.schedule.add_talkschedule or perms.schedule.change_talkschedule %} + + Create/Add to the Schedule + + {% endif %} + + + + + + {% else %} + + + + My Profile + + + {% endif %}
+ +
+ + + + + + + + + {% if user_profile %} + + {% for user_p in user_profile %} + {% for detail in details %} +
+ + +
+
+

{{ user_p.surname }} {{ user_p.name }}

+ {% if user_p.profession %} {{ user_p.profession }} {% else %}{% endif %} {% if user_p.organization %} at {{ user_p.organization }}{% else %}{% endif %}. +
  • + from {{ user_p.city }}, {{ user_p.country.name }} +
  • +
    + + +
      + {% if user_p.website %} +
    • + + + + +
    • + {% else %} + {% endif %} + {% if user_p.twitter_handle %} +
    • + + + + +
    • + {% else %} + {% endif %} + {% if user_p.github_username %} +
    • + + + + +
    • + {% else %} + {% endif %} + {% if user_p.linkedin %} +
    • + + + + +
    • + {% else %} + {% endif %} + +
    + +
    + + +
    +

    {{ user_p.biography | markdown | safe }}

    + {{ form.media }} +
    + + +
    + + {% invitation_list %} + + +
    +
    + +
    + +
    +

    +    Speaking +

    +
    + + + +
    +
      +
    • +
      +
      +
      What it takes to speak at PyCon Africa
      +
      +

      We are inviting speakers of all experience levels and backgrounds to contribute to our conference program at PyCon Africa 2024! ...

      + +

      Read more...

      + + + + +
      +
    • +
    +
    + +
    + +
    + +
    + +
    +
    +

    +    My Talks +

    + Submit a Talk +
    + +
    + {% user_talks_summary request.user %} +
    + +
    + +
    +
    + + +
    + {% endfor %} + {% endfor %} + + {% else %} + +
    +
    +
    +

    Thanks for Signing up! @{% firstof user.get_short_name user.get_username %} 🥳

    +


    + We're thrilled to have you join this vibrant community of Python enthusiasts, developers, + and innovators from across Africa and beyond. To get started, we kindly ask you to create your profile. + Please click on the "Create Profile" button below to begin.

    🥂 Here's to an exciting journey ahead at PyCon Africa 2024! +

    + Create Profile » +
    +
    +
    + {% endif %} + +
    +
    +
    +
    + + + +{% endblock %} + + diff --git a/pycons-site/templates/profiles/login_details.html b/pycons-site/templates/profiles/login_details.html new file mode 100644 index 0000000..76036bb --- /dev/null +++ b/pycons-site/templates/profiles/login_details.html @@ -0,0 +1,24 @@ +{% extends "base2.html" %} +{% load crispy_forms_tags %} + +{% block content %} +
    +
    + +
    +
    +

    Update your login details

    +
    + +
    + {% crispy form form.helper %} + {% csrf_token %} +
    + +
    +
    + +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/pycons-site/templates/profiles/password_change.html b/pycons-site/templates/profiles/password_change.html new file mode 100644 index 0000000..f594f43 --- /dev/null +++ b/pycons-site/templates/profiles/password_change.html @@ -0,0 +1,24 @@ +{% extends "base2.html" %} +{% load crispy_forms_tags %} + +{% block content %} +
    +
    + +
    +
    +

    Change your password

    +
    + +
    + {% crispy form form.helper %} + {% csrf_token %} +
    + +
    +
    + +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/pycons-site/templates/profiles/profilepic_nav.html b/pycons-site/templates/profiles/profilepic_nav.html new file mode 100644 index 0000000..9770deb --- /dev/null +++ b/pycons-site/templates/profiles/profilepic_nav.html @@ -0,0 +1,13 @@ +{% load avatar_tags static profile_tags %} + +
    +
    + + + {% if request.user.profile.is_visible == True %} + + {% else %} + + {% endif %} +
    +
    \ No newline at end of file diff --git a/pycons-site/templates/profiles/profilepic_side.html b/pycons-site/templates/profiles/profilepic_side.html new file mode 100644 index 0000000..6fb45ec --- /dev/null +++ b/pycons-site/templates/profiles/profilepic_side.html @@ -0,0 +1,17 @@ +{% load avatar_tags static profile_tags %} + + +
    + {% if request.user.user_profile.profile_image %} +
    + Image Description +
    + {% else %} +
    + Image Description +
    + + {% endif %} +
    + + \ No newline at end of file diff --git a/pycons-site/templates/profiles/success.html b/pycons-site/templates/profiles/success.html new file mode 100644 index 0000000..e2cfccb --- /dev/null +++ b/pycons-site/templates/profiles/success.html @@ -0,0 +1,20 @@ +{% extends "base2.html" %} + +{% block content %} +
    +
    + +
    +
    +

    Profile Update Successful!

    +
    +

    Your changes have been updated successfully. +

    + +
    +
    + +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/pycons-site/templates/profiles/update.html b/pycons-site/templates/profiles/update.html new file mode 100644 index 0000000..6d99e39 --- /dev/null +++ b/pycons-site/templates/profiles/update.html @@ -0,0 +1,149 @@ +{% extends "base.html" %} +{% load i18n static avatar_tags crispy_forms_tags countries profile_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Update Profile || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + + + +
    +
    +

    About Us

    + +
    + +
    + +
    + + + +
    +
    +
    +
    + + +
    + + {% include 'profiles/profilepic_side.html' %} + + + +
    +
    +
    +
    LAST LOGIN
    +
    Date: {{ request.user.last_login.date }}
    +
    Time: {{ request.user.last_login.time }}
    +
    +
    +
    + + + + My Profile + + + + + + Update my Profile + + + + + + Change Password + + + + + + My Submitted Talks + + + + + + Submit a new Talk + + + + {% if perms.reviews.add_review %} + + + Review Proposals + + + {% endif %} + + + + {% if request.user.is_superuser %} + + + Proposals by Category + + + {% endif %} + + +
    + + + + +
    +
    +
    + +
    +

    Update your details below


    +
    {% csrf_token %} + + {{ form|crispy }} + {{ form.media }} +
    +
    + + + +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    +
    + + + + + +{% endblock %} + + \ No newline at end of file diff --git a/pycons-site/templates/registration/activate.html b/pycons-site/templates/registration/activate.html new file mode 100644 index 0000000..3a7a136 --- /dev/null +++ b/pycons-site/templates/registration/activate.html @@ -0,0 +1,18 @@ +{% extends "registration/registration_base.html" %} +{% load i18n %} + +{% block title %}{% trans "Activation Failure" %}{% endblock %} + +{% block content %} +

    {% trans "Account activation failed." %}

    +{% endblock %} + + +{% comment %} +**registration/activate.html** + +Used if account activation fails. With the default setup, has the following context: + +``activation_key`` + The activation key used during the activation attempt. +{% endcomment %} diff --git a/pycons-site/templates/registration/activation_complete.html b/pycons-site/templates/registration/activation_complete.html new file mode 100644 index 0000000..f43a4ba --- /dev/null +++ b/pycons-site/templates/registration/activation_complete.html @@ -0,0 +1,72 @@ +{% extends "base.html" %} +{% load i18n static crispy_forms_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Account Activated || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + + +
    +
    +
    +
    +

    +

    +

    +
    + +
    +
    +
    + + + + + +
    +
    +
    +
    +
    +
    +

    Account Activated

    +
    +
    +
    +

    Activation completed! 😊


    + {% if not user.is_authenticated %} + + {% endif %} +
    + + +
    +
    +
    +
    +
    +
    + + + + + + +{% endblock %} + + + + +{% comment %} +**registration/activation_complete.html** + +Used after successful account activation. This template has no context +variables of its own, and should simply inform the user that their +account is now active. +{% endcomment %} diff --git a/pycons-site/templates/registration/activation_complete_admin_pending.html b/pycons-site/templates/registration/activation_complete_admin_pending.html new file mode 100644 index 0000000..25357ca --- /dev/null +++ b/pycons-site/templates/registration/activation_complete_admin_pending.html @@ -0,0 +1,22 @@ +{% extends "registration/registration_base.html" %} +{% load i18n %} + +{% block title %}{% trans "Account Activated" %}{% endblock %} + +{% block content %} +

    + {% trans "Your account is now activated." %} + {% if not user.is_authenticated %} + {% trans "Once a site administrator activates your account you can login." %} + {% endif %} +

    +{% endblock %} + + +{% comment %} +**registration/activation_complete.html** + +Used after successful account activation. This template has no context +variables of its own, and should simply inform the user that their +account is now active. +{% endcomment %} diff --git a/pycons-site/templates/registration/activation_email.html b/pycons-site/templates/registration/activation_email.html new file mode 100644 index 0000000..2c7b78e --- /dev/null +++ b/pycons-site/templates/registration/activation_email.html @@ -0,0 +1,77 @@ +{% load i18n %} + + + + + {{ site.name }} {% trans "registration" %} + + + + +

    + {% blocktrans with site_name=site.name %} + Hello there, +

    + You (or someone pretending to be you) have asked to register an account on + {{ site_name }}'s website.
    If this wasn't you, please ignore this email + and your address will be removed from our records. + {% endblocktrans %} +

    +

    + {% blocktrans %} + But if you are the one and want to activate your account, please click the following link within the next + {{ expiration_days }} days: + {% endblocktrans %} +

    + +

    + + {{site.domain}}{% url 'registration_activate' activation_key %} + +

    +

    + {% blocktrans with site_name=site.name %} + Sincerely,
    + {{ site_name }} +
    + Twitter | Instagram
    Python Africa's website + {% endblocktrans %} +

    + + + + + +{% comment %} +**registration/activation_email.html** + +Used to generate the html body of the activation email. Should display a +link the user can click to activate the account. This template has the +following context: + +``activation_key`` + The activation key for the new account. + +``expiration_days`` + The number of days remaining during which the account may be + activated. + +``site`` + An object representing the site on which the user registered; + depending on whether ``django.contrib.sites`` is installed, this + may be an instance of either ``django.contrib.sites.models.Site`` + (if the sites application is installed) or + ``django.contrib.sites.requests.RequestSite`` (if not). Consult `the + documentation for the Django sites framework + `_ for + details regarding these objects' interfaces. + +``user`` + The new user account + +``request`` + ``HttpRequest`` instance for better flexibility. + For example it can be used to compute absolute register URL: + + {{ request.scheme }}://{{ request.get_host }}{% url 'registration_activate' activation_key %} +{% endcomment %} diff --git a/pycons-site/templates/registration/activation_email.txt b/pycons-site/templates/registration/activation_email.txt new file mode 100644 index 0000000..303d7ed --- /dev/null +++ b/pycons-site/templates/registration/activation_email.txt @@ -0,0 +1,52 @@ +{% load i18n %} +{% blocktrans with site_name=site.name %} +You (or someone pretending to be you) have asked to register an account at +{{ site_name }}. If this wasn't you, please ignore this email +and your address will be removed from our records. +{% endblocktrans %} +{% blocktrans %} +To activate this account, please click the following link within the next +{{ expiration_days }} days: +{% endblocktrans %} + +http://{{site.domain}}{% url 'registration_activate' activation_key %} + +{% blocktrans with site_name=site.name %} +Sincerely, +{{ site_name }} Management +{% endblocktrans %} + + +{% comment %} +**registration/activation_email.txt** + +Used to generate the text body of the activation email. Should display a +link the user can click to activate the account. This template has the +following context: + +``activation_key`` + The activation key for the new account. + +``expiration_days`` + The number of days remaining during which the account may be + activated. + +``site`` + An object representing the site on which the user registered; + depending on whether ``django.contrib.sites`` is installed, this + may be an instance of either ``django.contrib.sites.models.Site`` + (if the sites application is installed) or + ``django.contrib.sites.requests.RequestSite`` (if not). Consult `the + documentation for the Django sites framework + `_ for + details regarding these objects' interfaces. + +``user`` + The new user account + +``request`` + ``HttpRequest`` instance for better flexibility. + For example it can be used to compute absolute register URL: + + {{ request.scheme }}://{{ request.get_host }}{% url 'registration_activate' activation_key %} +{% endcomment %} diff --git a/pycons-site/templates/registration/activation_email_subject.txt b/pycons-site/templates/registration/activation_email_subject.txt new file mode 100644 index 0000000..da0ddeb --- /dev/null +++ b/pycons-site/templates/registration/activation_email_subject.txt @@ -0,0 +1,28 @@ +{% load i18n %}{% trans "Account activation on" %} {{ site.name }} + + +{% comment %} +**registration/activation_email_subject.txt** + +Used to generate the subject line of the activation email. Because the +subject line of an email must be a single line of text, any output +from this template will be forcibly condensed to a single line before +being used. This template has the following context: + +``activation_key`` + The activation key for the new account. + +``expiration_days`` + The number of days remaining during which the account may be + activated. + +``site`` + An object representing the site on which the user registered; + depending on whether ``django.contrib.sites`` is installed, this + may be an instance of either ``django.contrib.sites.models.Site`` + (if the sites application is installed) or + ``django.contrib.sites.requests.RequestSite`` (if not). Consult `the + documentation for the Django sites framework + `_ for + details regarding these objects' interfaces. +{% endcomment %} diff --git a/pycons-site/templates/registration/admin_approve.html b/pycons-site/templates/registration/admin_approve.html new file mode 100644 index 0000000..487c1fb --- /dev/null +++ b/pycons-site/templates/registration/admin_approve.html @@ -0,0 +1,18 @@ +{% extends "registration/registration_base.html" %} +{% load i18n %} + +{% block title %}{% trans "Approval Failure" %}{% endblock %} + +{% block content %} +

    {% trans "Account Approval failed." %}

    +{% endblock %} + + +{% comment %} +**registration/admin_approve.html** + +Used if account activation fails. With the default setup, has the following context: + +``activation_key`` + The activation key used during the activation attempt. +{% endcomment %} diff --git a/pycons-site/templates/registration/admin_approve_complete.html b/pycons-site/templates/registration/admin_approve_complete.html new file mode 100644 index 0000000..17ab434 --- /dev/null +++ b/pycons-site/templates/registration/admin_approve_complete.html @@ -0,0 +1,19 @@ +{% extends "registration/registration_base.html" %} +{% load i18n %} + +{% block title %}{% trans "Account Approved" %}{% endblock %} + +{% block content %} +

    + {% trans "The user's account is now approved." %} +

    +{% endblock %} + + +{% comment %} +**registration/admin_approve_complete.html** + +Used after admin successfully approves a user's account. This template has no context +variables of its own, and should simply inform the admin that the user's +account is now active. +{% endcomment %} diff --git a/pycons-site/templates/registration/admin_approve_complete_email.html b/pycons-site/templates/registration/admin_approve_complete_email.html new file mode 100644 index 0000000..d1f978f --- /dev/null +++ b/pycons-site/templates/registration/admin_approve_complete_email.html @@ -0,0 +1,27 @@ +{% load i18n %} + + + + + {{ site.name }} {% trans "admin approval" %} + + + +

    + {% blocktrans %} + Your account is now approved. You can + {% endblocktrans %} + {% trans "log in." %} +

    + + + + + +{% comment %} +**registration/admin_approve_complete_email.html** + +Used after successful account activation. This template has no context +variables of its own, and should simply inform the user that their +account is now active. +{% endcomment %} diff --git a/pycons-site/templates/registration/admin_approve_complete_email.txt b/pycons-site/templates/registration/admin_approve_complete_email.txt new file mode 100644 index 0000000..97d6dee --- /dev/null +++ b/pycons-site/templates/registration/admin_approve_complete_email.txt @@ -0,0 +1,13 @@ +{% load i18n %} +{% blocktrans %} +Your account is now approved. You can log in using the following link +{% endblocktrans %} +http://{{site.domain}}{% url 'login' %} + +{% comment %} +**registration/admin_approve_complete_email.txt** + +Used after successful account activation. This template has no context +variables of its own, and should simply inform the user that their +account is now active. +{% endcomment %} diff --git a/pycons-site/templates/registration/admin_approve_complete_email_subject.txt b/pycons-site/templates/registration/admin_approve_complete_email_subject.txt new file mode 100644 index 0000000..6dac1db --- /dev/null +++ b/pycons-site/templates/registration/admin_approve_complete_email_subject.txt @@ -0,0 +1,21 @@ +{% load i18n %}{% trans "Account activation on" %} {{ site.name }} + + +{% comment %} +**registration/admin_approve_complete_email_subject.txt** + +Used to generate the subject line of the admin approval complete email. Because +the subject line of an email must be a single line of text, any output +from this template will be forcibly condensed to a single line before +being used. This template has the following context: + +``site`` + An object representing the site on which the user registered; + depending on whether ``django.contrib.sites`` is installed, this + may be an instance of either ``django.contrib.sites.models.Site`` + (if the sites application is installed) or + ``django.contrib.sites.requests.RequestSite`` (if not). Consult `the + documentation for the Django sites framework + `_ for + details regarding these objects' interfaces. +{% endcomment %} diff --git a/pycons-site/templates/registration/admin_approve_email.html b/pycons-site/templates/registration/admin_approve_email.html new file mode 100644 index 0000000..a2f3cae --- /dev/null +++ b/pycons-site/templates/registration/admin_approve_email.html @@ -0,0 +1,30 @@ +{% load i18n %} + + + + + {{ site.name }} {% trans "registration" %} + + + +

    + {% blocktrans with site_name=site.name %} + The following user ({{ user }}) has asked to register an account at + {{ site_name }}. + {% endblocktrans %} +

    +

    + {% blocktrans %} + To approve this, please + {% endblocktrans %} + {% trans "click here" %}. +

    +

    + {% blocktrans with site_name=site.name %} + Sincerely, + {{ site_name }} Management + {% endblocktrans %} +

    + + + diff --git a/pycons-site/templates/registration/admin_approve_email.txt b/pycons-site/templates/registration/admin_approve_email.txt new file mode 100644 index 0000000..19ac274 --- /dev/null +++ b/pycons-site/templates/registration/admin_approve_email.txt @@ -0,0 +1,10 @@ +{% load i18n %} +{% blocktrans with site_name=site.name %} +The following user ({{ user }}) has asked to register an account at +{{ site_name }}. +{% endblocktrans %} +{% blocktrans %} +To approve this, please click the following link. +{% endblocktrans %} + +http://{{site.domain}}{% url 'registration_admin_approve' profile_id %} diff --git a/pycons-site/templates/registration/admin_approve_email_subject.txt b/pycons-site/templates/registration/admin_approve_email_subject.txt new file mode 100644 index 0000000..3cd2bd9 --- /dev/null +++ b/pycons-site/templates/registration/admin_approve_email_subject.txt @@ -0,0 +1 @@ +{% load i18n %}{% trans "Account approval on" %} {{ site.name }} diff --git a/pycons-site/templates/registration/log_base.html b/pycons-site/templates/registration/log_base.html new file mode 100644 index 0000000..a0b2757 --- /dev/null +++ b/pycons-site/templates/registration/log_base.html @@ -0,0 +1,172 @@ +{% load static crispy_forms_tags %} + + + + + + + + + + + + + {% block meta_title %}{% endblock %}{% if settings.SITE_TITLE %} | {{ settings.SITE_TITLE }}{% endif %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + +
    +
    + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pycons-site/templates/registration/login.html b/pycons-site/templates/registration/login.html new file mode 100644 index 0000000..64224b1 --- /dev/null +++ b/pycons-site/templates/registration/login.html @@ -0,0 +1,69 @@ +{% extends "base.html" %} +{% load i18n static current_year crispy_forms_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Log in || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + + +
    +
    +
    +
    +
    +
    +

    Login

    +
    + + + + + + + + + + +
    +
    +
    +
    +
    + + +{% endblock %} diff --git a/pycons-site/templates/registration/logout.html b/pycons-site/templates/registration/logout.html new file mode 100644 index 0000000..88f92e0 --- /dev/null +++ b/pycons-site/templates/registration/logout.html @@ -0,0 +1,32 @@ +{% extends "registration/log_base.html" %} +{% load i18n static crispy_forms_tags %} + + +{% block title %}{% trans "Logged out" %}{% endblock %} + +{% block content %} +
    +
    + +
    +
    +
    +
    +
    You are logged out
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +{% endblock %} diff --git a/pycons-site/templates/registration/password_change_done.html b/pycons-site/templates/registration/password_change_done.html new file mode 100644 index 0000000..f886aa4 --- /dev/null +++ b/pycons-site/templates/registration/password_change_done.html @@ -0,0 +1,11 @@ +{% extends "registration/registration_base.html" %} +{% load i18n %} + +{% block title %}{% trans "Password changed" %}{% endblock %} + +{% block content %} +

    {% trans "Password successfully changed!" %}

    +{% endblock %} + + +{# This is used by django.contrib.auth #} diff --git a/pycons-site/templates/registration/password_change_form.html b/pycons-site/templates/registration/password_change_form.html new file mode 100644 index 0000000..b8c9c99 --- /dev/null +++ b/pycons-site/templates/registration/password_change_form.html @@ -0,0 +1,150 @@ +{% extends "base.html" %} +{% load i18n static avatar_tags crispy_forms_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Update Password || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + + + + + +
    +
    +

    Update your password

    + +
      +
    • + Home + / +
    • +
    • + Profile + / +
    • +
    • + Update your password +
    • +

    + +
    + +
    + + + +
    +
    +
    +
    + + +
    + + {% include 'profiles/profilepic_side.html' %} + + + +
    +
    +
    +
    LAST LOGIN
    +
    Date: {{ request.user.last_login.date }}
    +
    Time: {{ request.user.last_login.time }}
    +
    +
    +
    + + + + My Profile + + + + + + Update my Profile + + + + + + Change Password + + + + + + My Submitted Talks + + + + + + Submit a new Talk + + + + {% if perms.reviews.add_review %} + + + Review Proposals + + + {% endif %} + + + + {% if request.user.is_superuser %} + + + Proposals by Category + + + {% endif %} + + +
    + + + +
    +
    +
    + +
    +

    Update your password


    +
    {% csrf_token %} + + {{ form|crispy }} + {{ form.media }} +
    +
    + + + +
    +
    +
    + + +
    +
    +
    + +
    +
    +
    +
    + + + + + +{% endblock %} + +{# This is used by django.contrib.auth #} diff --git a/pycons-site/templates/registration/password_reset_complete.html b/pycons-site/templates/registration/password_reset_complete.html new file mode 100644 index 0000000..83bfe23 --- /dev/null +++ b/pycons-site/templates/registration/password_reset_complete.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block title %}{% trans "Password reset complete" %}{% endblock %} + +{% block content %} +

    + {% trans "Your password has been reset!" %} + {% blocktrans %}You may now log in{% endblocktrans %}. + +

    +{% endblock %} + + +{# This is used by django.contrib.auth #} diff --git a/pycons-site/templates/registration/password_reset_confirm.html b/pycons-site/templates/registration/password_reset_confirm.html new file mode 100644 index 0000000..6c9d072 --- /dev/null +++ b/pycons-site/templates/registration/password_reset_confirm.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block meta %} + + +{% endblock %} + +{% block title %}{% trans "Confirm password reset" %}{% endblock %} + +{% block content %} +{% if validlink %} +

    {% trans "Enter your new password below to reset your password:" %}

    +
    + {% csrf_token %} + {{ form.as_p }} + +
    +{% else %} + Password reset unsuccessful. Please try again. +{% endif %} +{% endblock %} + + +{# This is used by django.contrib.auth #} diff --git a/pycons-site/templates/registration/password_reset_done.html b/pycons-site/templates/registration/password_reset_done.html new file mode 100644 index 0000000..029eabc --- /dev/null +++ b/pycons-site/templates/registration/password_reset_done.html @@ -0,0 +1,60 @@ +{% extends "base.html" %} +{% load i18n static crispy_forms_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Password Reset || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + + +
    +
    +

    Forgot Password

    + +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +

    Password reset

    +

    + We have sent an email to your mail with further instructions. + Please check your email and click the link to continue. +

    + + +

    Log in

    +
    + +
    +
    +
    +
    +
    +
    +
    + +{% endblock %} + + + + + + +{# This is used by django.contrib.auth #} diff --git a/pycons-site/templates/registration/password_reset_email.html b/pycons-site/templates/registration/password_reset_email.html new file mode 100644 index 0000000..ec1604c --- /dev/null +++ b/pycons-site/templates/registration/password_reset_email.html @@ -0,0 +1,44 @@ +{% load i18n %} + + + + + {{ site.name }} {% trans "Forgot Password" %} + + + + +
    +{% blocktrans %}Greetings{% endblocktrans %} {% if user.get_full_name %}{{ user.get_full_name }}{% else %}{{ user }}{% endif %}, +

    +{% blocktrans %} +You are receiving this email because you (or someone pretending to be you) +requested that your password be reset on the {{ domain }} site. If you do not +wish to reset your password, please ignore this message. +{% endblocktrans %} +

    +{% blocktrans %} +To reset your password, please click the following link, or copy and paste it +into your web browser: +{% endblocktrans %} +
    + + {{ protocol }}://{{ domain }}{% url 'auth_password_reset_confirm' uid token %} + +

    +{% blocktrans %}Your username, in case you've forgotten:{% endblocktrans %} {{ user.get_username }} +
    + +

    + {% blocktrans with site_name=site.name %} + Best regards,
    + {{ site_name }} + PyCon Africa + Twitter | Instagram
    Python Africa's website + {% endblocktrans %} +

    + + + + + \ No newline at end of file diff --git a/pycons-site/templates/registration/password_reset_form.html b/pycons-site/templates/registration/password_reset_form.html new file mode 100644 index 0000000..f0812ac --- /dev/null +++ b/pycons-site/templates/registration/password_reset_form.html @@ -0,0 +1,74 @@ +{% extends "base.html" %} +{% load i18n static crispy_forms_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Reset password || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + + +
    +
    +
    +
    +

    +

    +

    +
    + +
    +
    +
    + + + + + +
    +
    +
    +
    +
    +
    +

    Reset password

    +
    +
    + + + + + + +
    +
    +
    +
    +
    +
    + + + +{% endblock %} + + + + + +{# This is used by django.contrib.auth #} diff --git a/pycons-site/templates/registration/registration_base.html b/pycons-site/templates/registration/registration_base.html new file mode 100644 index 0000000..f28a687 --- /dev/null +++ b/pycons-site/templates/registration/registration_base.html @@ -0,0 +1,177 @@ +{% load static crispy_forms_tags %} + + + + + + + + + + + + + PyCon Africa 2018 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + +
    +
    + + +
    + + +
    + + + + + back to top + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pycons-site/templates/registration/registration_closed.html b/pycons-site/templates/registration/registration_closed.html new file mode 100644 index 0000000..94daff1 --- /dev/null +++ b/pycons-site/templates/registration/registration_closed.html @@ -0,0 +1,8 @@ +{% extends "registration/registration_base.html" %} +{% load i18n %} + +{% block title %}{% trans "Registration is closed" %}{% endblock %} + +{% block content %} +

    {% trans "Sorry, but registration is closed at this moment. Come back later." %}

    +{% endblock %} diff --git a/pycons-site/templates/registration/registration_complete.html b/pycons-site/templates/registration/registration_complete.html new file mode 100644 index 0000000..65a9a99 --- /dev/null +++ b/pycons-site/templates/registration/registration_complete.html @@ -0,0 +1,68 @@ +{% extends "base.html" %} +{% load i18n static crispy_forms_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Log in || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + +
    +
    +
    +
    +

    +

    +

    +
    + +
    +
    +
    + + + + + +
    +
    +
    +
    +
    +
    +

    Activation email sent

    +
    +
    +
    +

    Activate your account 🙂

    +

    {% trans "Please check your email/spam to complete the registration process." %}

    + + +

    Resend activation code

    + +
    + +
    +
    +
    +
    +
    +
    + + + + +{% endblock %} + + + +{% comment %} +**registration/registration_complete.html** + +Used after successful completion of the registration form. This +template has no context variables of its own, and should simply inform +the user that an email containing account-activation information has +been sent. +{% endcomment %} diff --git a/pycons-site/templates/registration/registration_form.html b/pycons-site/templates/registration/registration_form.html new file mode 100644 index 0000000..f0658aa --- /dev/null +++ b/pycons-site/templates/registration/registration_form.html @@ -0,0 +1,76 @@ +{% extends "base.html" %} +{% load i18n static crispy_forms_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Sign up || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + + + + + + +
    +
    +
    +
    +
    +
    +

    Signup

    +
    + + + + + + + + + + + +
    +
    +
    +
    +
    + + + +{% endblock %} + + + diff --git a/pycons-site/templates/registration/resend_activation_complete.html b/pycons-site/templates/registration/resend_activation_complete.html new file mode 100644 index 0000000..e28f689 --- /dev/null +++ b/pycons-site/templates/registration/resend_activation_complete.html @@ -0,0 +1,64 @@ +{% extends "base.html" %} +{% load i18n static crispy_forms_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Log in || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + +
    +
    +
    +
    +

    +

    +

    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +

    Activation code resent

    +
    +
    +

    +

    + We have sent an email to {{ email }} with further instructions.

    + + +

    Log in

    +
    + +
    +
    +
    +
    +
    + + + +{% endblock %} + + + + + +{% comment %} +**registration/resend_activation_complete.html** +Used after form for resending account activation is submitted. By default has +the following context: + +``email`` + The email address submitted in the resend activation form. +{% endcomment %} diff --git a/pycons-site/templates/registration/resend_activation_form.html b/pycons-site/templates/registration/resend_activation_form.html new file mode 100644 index 0000000..912e6e6 --- /dev/null +++ b/pycons-site/templates/registration/resend_activation_form.html @@ -0,0 +1,65 @@ +{% extends "base.html" %} +{% load i18n static crispy_forms_tags %} +{% block meta_title %}{% if page %}{{ page.meta_title }}{% else %}{% trans "Resend Activation Email || PyCon Africa" %}{% endif %}{% endblock %} + + +{% block content %} + + +{% include '2024/navbar.html' %} + + +
    +
    +
    +
    +

    +

    +

    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +

    Resend Activation Email

    +
    + + +
    +
    +
    +
    +
    + + + +{% endblock %} + + + + \ No newline at end of file diff --git a/pycons-site/templates/tem-nav.html b/pycons-site/templates/tem-nav.html new file mode 100644 index 0000000..8bad94e --- /dev/null +++ b/pycons-site/templates/tem-nav.html @@ -0,0 +1,201 @@ +{% load %} +{% load static base_extras %} + + + + + + + \ No newline at end of file diff --git a/pycons-site/tickets/__init__.py b/pycons-site/tickets/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/tickets/admin.py b/pycons-site/tickets/admin.py new file mode 100644 index 0000000..0f4e4f1 --- /dev/null +++ b/pycons-site/tickets/admin.py @@ -0,0 +1,14 @@ +# Core Django imports. +from django.contrib import admin + +# Blog application imports. +from .models import Ticket + +class TicketAdmin(admin.ModelAdmin): + + list_display = ('ticket_title', 'date_created') + ordering = ['-date_created', ] + +# Registers the Ticket model at the admin backend. +admin.site.register(Ticket, TicketAdmin) + diff --git a/pycons-site/tickets/apps.py b/pycons-site/tickets/apps.py new file mode 100644 index 0000000..3ea742a --- /dev/null +++ b/pycons-site/tickets/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class TicketsConfig(AppConfig): + name = 'tickets' diff --git a/pycons-site/tickets/forms.py b/pycons-site/tickets/forms.py new file mode 100644 index 0000000..586b2d7 --- /dev/null +++ b/pycons-site/tickets/forms.py @@ -0,0 +1,43 @@ +""" +Forms and validation code for user registration. + +Note that all of these forms assume Django's bundle default ``User`` +model; since it's not possible for a form to anticipate in advance the +needs of custom user models, you will need to write your own forms if +you're using a custom model. + +""" +from django import forms + +from registration.users import UserModel + +# Third Parties +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Submit +from django_recaptcha.fields import ReCaptchaField + + +User = UserModel() + + +from .models import Ticket + + + + +class TicketForm(forms.ModelForm): + captcha = ReCaptchaField() + + class Meta: + model = Ticket + fields = ('ticket_title', 'ticket_image_one', 'section_one', 'section_two', 'user',) + + def __init__(self, *args, **kwargs): + super(TicketForm, self).__init__(*args, **kwargs) + self.fields['user'].disabled = True + + self.helper = FormHelper() + self.helper.form_id = 'id-Crispy_TicketForm' + self.helper.form_class = 'form-horizontal' + self.helper.add_input(Submit('update', 'Ticket ')) + diff --git a/pycons-site/tickets/migrations/0001_initial.py b/pycons-site/tickets/migrations/0001_initial.py new file mode 100644 index 0000000..411577d --- /dev/null +++ b/pycons-site/tickets/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# Generated by Django 3.2 on 2022-08-03 02:38 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import markdownx.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Ticket', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ticket_title', models.CharField(help_text='Ticket PyCon Africa', max_length=250)), + ('ticket_image_one', models.ImageField(help_text='Upload your cover image or leave blank to use our default image', upload_to='ticket_page')), + ('ticket_image_two', models.URLField(default='', help_text='Link to image')), + ('section_one', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - Ticket PyCon Africa.')), + ('section_two', markdownx.models.MarkdownxField(default='', help_text='[Supports Markdown] - Ticket PyCon Africa.')), + ('date_created', models.DateTimeField(auto_now_add=True)), + ('date_updated', models.DateTimeField(auto_now=True)), + ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ticket_us', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/pycons-site/tickets/migrations/0002_alter_ticket_ticket_title.py b/pycons-site/tickets/migrations/0002_alter_ticket_ticket_title.py new file mode 100644 index 0000000..f68ebce --- /dev/null +++ b/pycons-site/tickets/migrations/0002_alter_ticket_ticket_title.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2 on 2022-08-03 02:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='ticket', + name='ticket_title', + field=models.CharField(blank=True, help_text='Ticket PyCon Africa', max_length=250, null=True), + ), + ] diff --git a/pycons-site/tickets/migrations/0003_auto_20220803_0304.py b/pycons-site/tickets/migrations/0003_auto_20220803_0304.py new file mode 100644 index 0000000..5dc189f --- /dev/null +++ b/pycons-site/tickets/migrations/0003_auto_20220803_0304.py @@ -0,0 +1,33 @@ +# Generated by Django 3.2 on 2022-08-03 03:04 + +from django.db import migrations, models +import markdownx.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0002_alter_ticket_ticket_title'), + ] + + operations = [ + migrations.RemoveField( + model_name='ticket', + name='ticket_image_two', + ), + migrations.AlterField( + model_name='ticket', + name='section_one', + field=markdownx.models.MarkdownxField(blank=True, default='', help_text='[Supports Markdown] - Ticket PyCon Africa.', null=True), + ), + migrations.AlterField( + model_name='ticket', + name='section_two', + field=markdownx.models.MarkdownxField(blank=True, default='', help_text='[Supports Markdown] - Ticket PyCon Africa.', null=True), + ), + migrations.AlterField( + model_name='ticket', + name='ticket_image_one', + field=models.ImageField(blank=True, help_text='Upload your cover image or leave blank to use our default image', null=True, upload_to='ticket_page'), + ), + ] diff --git a/pycons-site/tickets/migrations/0004_alter_ticket_ticket_image_one.py b/pycons-site/tickets/migrations/0004_alter_ticket_ticket_image_one.py new file mode 100644 index 0000000..91433af --- /dev/null +++ b/pycons-site/tickets/migrations/0004_alter_ticket_ticket_image_one.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2 on 2022-08-14 13:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0003_auto_20220803_0304'), + ] + + operations = [ + migrations.AlterField( + model_name='ticket', + name='ticket_image_one', + field=models.ImageField(blank=True, default='tickets.png', help_text='Upload your cover image or leave blank to use our default image', null=True, upload_to='ticket_page'), + ), + ] diff --git a/pycons-site/tickets/migrations/0005_alter_ticket_id.py b/pycons-site/tickets/migrations/0005_alter_ticket_id.py new file mode 100644 index 0000000..d0f0381 --- /dev/null +++ b/pycons-site/tickets/migrations/0005_alter_ticket_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-02-29 23:00 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0004_alter_ticket_ticket_image_one'), + ] + + operations = [ + migrations.AlterField( + model_name='ticket', + name='id', + field=models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/pycons-site/tickets/migrations/__init__.py b/pycons-site/tickets/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pycons-site/tickets/mixins.py b/pycons-site/tickets/mixins.py new file mode 100644 index 0000000..9283632 --- /dev/null +++ b/pycons-site/tickets/mixins.py @@ -0,0 +1,23 @@ +from django.core.exceptions import PermissionDenied + +class EditOwnTicketMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnTicketMixin, self).get_object(*args, **kwargs) + self.check_permission(obj) + return obj + def check_permission(self, obj): + if hasattr(obj, 'user'): + obj = obj.user + if obj == self.request.user: + return + else: + raise PermissionDenied() + +class EditOwnLoginMixin(): + def get_object(self, *args, **kwargs): + obj = super(EditOwnLoginMixin, self).get_object(*args, **kwargs) + if obj.email == self.request.user.email: + return obj + else: + raise PermissionDenied + diff --git a/pycons-site/tickets/models.py b/pycons-site/tickets/models.py new file mode 100644 index 0000000..8b8b180 --- /dev/null +++ b/pycons-site/tickets/models.py @@ -0,0 +1,41 @@ +from django.db import models + +# Create your models here. +from django.db import models +from django.urls import reverse +from django.conf import settings +from django.contrib.auth.models import User +from markdownx.models import MarkdownxField +from django.utils import timezone +from home.models import EventYear +from PIL import Image +from io import BytesIO +import os +from django.core.files.base import ContentFile + + + + +class Ticket(models.Model): + ticket_title = models.CharField(max_length=250, null=True, blank=False, help_text='Ticket PyCon Africa') + ticket_image_one = models.ImageField(help_text="Upload your cover image or leave blank to use our default image", default="tickets.png", null=True, blank=True, upload_to='ticket_page') + section_one = MarkdownxField(default='', help_text = "[Supports Markdown] - Section One.", null=True, blank=True) + section_two = MarkdownxField(default='', help_text = "[Supports Markdown] - Section Two", null=True, blank=True) + embedded_codes = MarkdownxField(default='', help_text = "[Supports Markdown] - If the Ticket Platform allows you to embed the tickets into your site.", null=True, blank=True) + donation_link = models.CharField(max_length=250, null=True, blank=True, help_text='Donation for PyCon Africa if any') + user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, null=True, related_name='ticket_us', on_delete=models.CASCADE) + event_year = models.ForeignKey(EventYear, on_delete=models.CASCADE, default="2024", related_name='tickets') + date_created = models.DateTimeField(auto_now_add=True) + date_updated = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.ticket_title + + def get_absolute_url(self): + return reverse("ticket_detail", kwargs={"year": self.event_year.year, "pk": self.pk}) + + @property + def image_url(self): + if self.ticket_image_one: + return self.ticket_image_one.url + return os.path.join(settings.STATIC_URL, 'default_image.png') \ No newline at end of file diff --git a/pycons-site/tickets/tests.py b/pycons-site/tickets/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/pycons-site/tickets/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/pycons-site/tickets/urls.py b/pycons-site/tickets/urls.py new file mode 100644 index 0000000..9dee41c --- /dev/null +++ b/pycons-site/tickets/urls.py @@ -0,0 +1,8 @@ +from django.urls import path +from . import views + +app_name = 'tickets' +urlpatterns = [ + path('', view=views.ticket, name='ticket'), + path('register', view=views.register, name='register'), +] diff --git a/pycons-site/tickets/views.py b/pycons-site/tickets/views.py new file mode 100644 index 0000000..b629a0c --- /dev/null +++ b/pycons-site/tickets/views.py @@ -0,0 +1,91 @@ +#home views + +# Core Django imports. +from django.utils import timezone + +# Ticket application imports. +from .models import Ticket + +# Included by me (3rd Parties and more) +from datetime import datetime +from .forms import TicketForm + +from django.shortcuts import get_object_or_404, render, redirect +from django.views.generic.edit import UpdateView +from django.urls import reverse_lazy +from home.models import EventYear +from tickets.mixins import EditOwnTicketMixin + + +# Create your views here. + +def ticket(request, year): + event_year = get_object_or_404(EventYear, year=year) + tickets = Ticket.objects.filter(event_year=event_year).order_by('-date_created') + if tickets.exists(): + first_ticket = tickets.first() + meta_og_image = first_ticket.image_url + else: + meta_og_image = 'default_image_url' + + context = { + 'tickets': tickets, + 'event_year': event_year, + 'meta_title': f"Tickets for PyCon Africa {year}", + 'meta_description': "Join us at PyCon Africa! Get your tickets for this year's event and don't miss out.", + 'meta_author': "PyCon Africa", + 'meta_og_image': meta_og_image, + } + template_name = f'{year}/tickets/tickets.html' # Dynamically set based on the year + return render(request, template_name, context) + + + +def ticket_edit(request, pk): + ticket = get_object_or_404(Ticket, pk=pk) + if request.method == "POST": + form = TicketForm(request.POST, instance=ticket) + if form.is_valid(): + ticket = form.save(commit=False) + ticket.user = request.user + ticket.date_updated = timezone.now() + ticket.save() + return redirect('ticket:ticket_home') + else: + form = TicketForm(instance=ticket) + return render(request, '2022/ticket/update_ticket.html', {'form': form}) + + +class TicketView(EditOwnTicketMixin, UpdateView): + form_class = TicketForm + model = Ticket + template_name = "2022/ticket/update_ticket.html" + success_url = reverse_lazy('ticket:ticket_home') + + def get_context_data(self, **kwargs): + context = super(UpdateView, self).get_context_data(**kwargs) + context['title'] = 'Ticket' + context['year'] = datetime.now().year + try: + context['ticket'] = Ticket() + except Ticket.DoesNotExist: + context['ticket'] = '' + return context + +def ticket_update_view(UpdateView): + obj = get_object_or_404(Ticket, id=id) + form = TicketForm(UpdateView.POST or None, instance=obj) + if form.is_valid(): + form.save() + context = { + 'form': form + } + return render(UpdateView, "2022/ticket/update_ticket.html", context) + + + + +def register(request): + context = {} + template = '2022/ticket/register.html' + return render(request, template, context) \ No newline at end of file diff --git a/pycons-site/urls.py b/pycons-site/urls.py new file mode 100644 index 0000000..99038d7 --- /dev/null +++ b/pycons-site/urls.py @@ -0,0 +1,80 @@ +"""pyconafrica URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/3.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.conf import settings +from django.conf.urls.static import static +from django.contrib import admin +from django.urls import include, path, re_path +from django.conf.urls import handler404, handler500 +from django_robohash.views import robohash +from django.views.static import serve +from django.views.generic import RedirectView + + +urlpatterns = [ + +#Apps + path('', include('home.urls', namespace='homepage')), + path('/', include([ + path('', include('home.urls')), + path('about/', include('about.urls')), + path('speakers/', include('speakers.urls')), + path('schedule/', include('conference_schedule.urls')), + path('our-sponsors/', include('sponsors.urls', namespace='sponsors')), + path('talks/', include('talks.urls', namespace='talks')), + path('coc/', include('coc.urls')), + path('sponsor-us/', include('sponsor_us.urls', namespace='sponsor_us')), + # Add more apps here following the same pattern + path('h&g/', include('health_safety_guideline.urls', namespace='health_safety_guideline')), + path('fin-aid/', include('fin_aid.urls', namespace='fin_aid')), + path('privacy-policy/', include('privacypolicy.urls', namespace='privacypolicy')), + path('tickets/', include('tickets.urls', namespace='ticket')), + + + #Leave this last to catch all pages + path('', include('cms.urls')), + ])), + path('organizers/', admin.site.urls), + +#Thrid party Apps + path('summernote/', include('django_summernote.urls')), + path('tinymce/', include('tinymce.urls')), + path('accounts/profile/', include('registration.urls', namespace='profiles')), + path('accounts/', include('registration.backends.default.urls')), + path('grappelli/', include('grappelli.urls')), # grappelli URLS + path('robohash//', robohash, name='robohash'), + path('avatar/', include('avatar.urls')), + path('markdownx/', include('markdownx.urls')), + re_path(r'hitcount/', include('hitcount.urls', namespace='hitcount')), + + +] +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + + +# Adds ``STATIC_URL`` to the context of error pages, so that error +# pages can use JS, CSS and images. + +handler404 = 'home.views.handler404' + + + + +# Modifies default django admin titles and headers with custom app detail. +admin.site.site_header = "PyCon Africa Admin" +admin.site.site_title = "PyCon Africa Admin Portal" +admin.site.index_title = "Welcome to PyCon Africa Portal" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ba1591d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,69 @@ +[tool.poetry] +name = "pycons´site" +version = "0.1.0" +description = "Backend for the pycons website" +readme = "README.md" +package-mode = true +authors = [ + "Mannie Young ", +] + +keywords = ["event", "conference", "pycon", "website", "backend"] +classifiers = [ + + 'Environment :: Web Environment', + 'Framework :: Django', + 'Intended Audience :: Developers', + "Topic :: Software Development :: Libraries :: Python Modules", + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Topic :: Internet :: WWW/HTTP', +] +packages = [{ include = "pycons-site" }] + +exclude = [ +"templates/*", +"pyconafrica/*", +"pyconafrica2019/*", +"pycon2020/*", +"static/*", +"media/*", +"db.sqlite3" +] + +[tool.poetry.dependencies] +python = "^3.11" +django = "^5.0.3" +cloudinary = "^1.39.1" +django-grappelli = "^3.0.8" +django-cloudinary-storage = "^0.3.0" +django-gamma-cloudinary = "^0.2.3" +django-avatar = "^8.0.0" +django-crispy-forms = "^2.1" +crispy-bootstrap5 = "^2024.2" +django-recaptcha = "^4.0.0" +django-slugify-processor = "^1.6.0" +django-countries = "^7.6" +django-summernote = "^0.8.20.0" +django-extensions = "^3.2.3" +django-robohash-svg = "^0.9.5" +django-embed-video = "^1.4.9" +django-imagekit = "^5.0.0" +django-import-export = "^3.3.7" +djangorestframework = "^3.15.1" +django-tinymce = "^4.0.0" +sorl-thumbnail = "^12.10.0" +django-markdownx = "^4.0.7" +django-hitcount = "^1.3.5" +django-taggit = "^5.0.1" +django-thumbs-v2 = "^0.4.1" +django-simple-history = "^3.5.0" +hashids = "^1.3.1" +django-hashid-field = "^3.4.0" +django-next-prev = "^1.1.0" +pytz = "^2024.1" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" \ No newline at end of file