From f0641be55ca294cbeb03a65fedab6261a734d8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Ku=C5=BE=C3=ADlek?= Date: Wed, 4 Dec 2019 19:53:00 +0100 Subject: [PATCH] Moving on --- Pipfile | 10 +- Pipfile.lock | 292 +++++++++------- yadc/__init__.py | 18 +- yadc/auth.py | 40 +++ yadc/main.py | 40 +++ yadc/models.py | 23 +- yadc/static/_include-media.scss | 567 ++++++++++++++++++++++++++++++++ yadc/static/default.scss | 356 ++++++++++++++++++++ yadc/templates/index.html | 254 ++++++++++++++ yadc/templates/post.html | 144 ++++++++ 10 files changed, 1616 insertions(+), 128 deletions(-) create mode 100644 yadc/auth.py create mode 100644 yadc/main.py create mode 100644 yadc/static/_include-media.scss create mode 100644 yadc/static/default.scss create mode 100644 yadc/templates/index.html create mode 100644 yadc/templates/post.html diff --git a/Pipfile b/Pipfile index 88baf6a..0516fc0 100644 --- a/Pipfile +++ b/Pipfile @@ -10,12 +10,16 @@ rope = "*" [packages] flask = "*" -sqlalchemy = "*" flask-sqlalchemy = "*" flask-migrate = "*" -alembic = "*" pillow = "*" psycopg2 = "*" +flask-login = "*" +flask-wtf = "*" +flask-assets = "*" +pyscss = "*" +libsass = "*" +cssmin = "*" [requires] -python_version = "3.7" +python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock index 996efbe..63828d8 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,11 +1,11 @@ { "_meta": { "hash": { - "sha256": "3f46403e414b456bf1041f33aefa6fe65adfd30537269cdb23e8c055f3fec29d" + "sha256": "2b4208c857a995a746d67b2b523ebd5caface2c836881c0b549e9474d200c507" }, "pipfile-spec": 6, "requires": { - "python_version": "3.7" + "python_version": "3.8" }, "sources": [ { @@ -18,10 +18,9 @@ "default": { "alembic": { "hashes": [ - "sha256:4a4811119efbdc5259d1f4c8f6de977b36ad3bcc919f59a29c2960c5ef9149e4" + "sha256:49277bb7242192bbb9eac58fed4fe02ec6c3a2a4b4345d2171197459266482b2" ], - "index": "pypi", - "version": "==1.1.0" + "version": "==1.3.1" }, "click": { "hashes": [ @@ -30,6 +29,13 @@ ], "version": "==7.0" }, + "cssmin": { + "hashes": [ + "sha256:e012f0cc8401efcf2620332339011564738ae32be8c84b2e43ce8beaec1067b6" + ], + "index": "pypi", + "version": "==0.2.0" + }, "flask": { "hashes": [ "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", @@ -38,6 +44,20 @@ "index": "pypi", "version": "==1.1.1" }, + "flask-assets": { + "hashes": [ + "sha256:6031527b89fb3509d1581d932affa5a79dd348cfffb58d0aef99a43461d47847" + ], + "index": "pypi", + "version": "==0.12" + }, + "flask-login": { + "hashes": [ + "sha256:c815c1ac7b3e35e2081685e389a665f2c74d7e077cb93cecabaea352da4752ec" + ], + "index": "pypi", + "version": "==0.4.1" + }, "flask-migrate": { "hashes": [ "sha256:6fb038be63d4c60727d5dfa5f581a6189af5b4e2925bc378697b4f0a40cfb4e1", @@ -48,11 +68,19 @@ }, "flask-sqlalchemy": { "hashes": [ - "sha256:0c9609b0d72871c540a7945ea559c8fdf5455192d2db67219509aed680a3d45a", - "sha256:8631bbea987bc3eb0f72b1f691d47bd37ceb795e73b59ab48586d76d75a7c605" + "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", + "sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d" ], "index": "pypi", - "version": "==2.4.0" + "version": "==2.4.1" + }, + "flask-wtf": { + "hashes": [ + "sha256:5d14d55cfd35f613d99ee7cba0fc3fbbe63ba02f544d349158c14ca15561cc36", + "sha256:d9a9e366b32dcbb98ef17228e76be15702cd2600675668bca23f63a7947fd5ac" + ], + "index": "pypi", + "version": "==0.14.2" }, "itsdangerous": { "hashes": [ @@ -63,10 +91,32 @@ }, "jinja2": { "hashes": [ - "sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", - "sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b" + "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", + "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de" + ], + "version": "==2.10.3" + }, + "libsass": { + "hashes": [ + "sha256:003a65b4facb4c5dbace53fb0f70f61c5aae056a04b4d112a198c3c9674b31f2", + "sha256:0fd8b4337b3b101c6e6afda9112cc0dc4bacb9133b59d75d65968c7317aa3272", + "sha256:338e9ae066bf1fde874e335324d5355c52d2081d978b4f74fc59536564b35b08", + "sha256:4dcfd561fb100250b89496e1362b96f2cc804f689a59731eb0f94f9a9e144f4a", + "sha256:50778d4be269a021ba2bf42b5b8f6ff3704ab96a82175a052680bddf3ba7cc9f", + "sha256:6a51393d75f6e3c812785b0fa0b7d67c54258c28011921f204643b55f7355ec0", + "sha256:74acd9adf506142699dfa292f0e569fdccbd9e7cf619e8226f7117de73566e32", + "sha256:81a013a4c2a614927fd1ef7a386eddabbba695cbb02defe8f31cf495106e974c", + "sha256:845a9573b25c141164972d498855f4ad29367c09e6d76fad12955ad0e1c83013", + "sha256:8b5b6d1a7c4ea1d954e0982b04474cc076286493f6af2d0a13c2e950fbe0be95", + "sha256:9b59afa0d755089c4165516400a39a289b796b5612eeef5736ab7a1ebf96a67c", + "sha256:a7e685466448c9b1bf98243339793978f654a1151eb5c975f09b83c7a226f4c1", + "sha256:c93df526eeef90b1ea4799c1d33b6cd5aea3e9f4633738fb95c1287c13e6b404", + "sha256:e318f06f06847ff49b1f8d086ac9ebce1e63404f7ea329adab92f4f16ba0e00e", + "sha256:fc5f8336750f76f1bfae82f7e9e89ae71438d26fc4597e3ab4c05ca8fcd41d8a", + "sha256:fcb7ab4dc81889e5fc99cafbc2017bc76996f9992fc6b175f7a80edac61d71df" ], - "version": "==2.10.1" + "index": "pypi", + "version": "==0.19.4" }, "mako": { "hashes": [ @@ -109,59 +159,72 @@ }, "pillow": { "hashes": [ - "sha256:0804f77cb1e9b6dbd37601cee11283bba39a8d44b9ddb053400c58e0c0d7d9de", - "sha256:0ab7c5b5d04691bcbd570658667dd1e21ca311c62dcfd315ad2255b1cd37f64f", - "sha256:0b3e6cf3ea1f8cecd625f1420b931c83ce74f00c29a0ff1ce4385f99900ac7c4", - "sha256:365c06a45712cd723ec16fa4ceb32ce46ad201eb7bbf6d3c16b063c72b61a3ed", - "sha256:38301fbc0af865baa4752ddae1bb3cbb24b3d8f221bf2850aad96b243306fa03", - "sha256:3aef1af1a91798536bbab35d70d35750bd2884f0832c88aeb2499aa2d1ed4992", - "sha256:3fe0ab49537d9330c9bba7f16a5f8b02da615b5c809cdf7124f356a0f182eccd", - "sha256:45a619d5c1915957449264c81c008934452e3fd3604e36809212300b2a4dab68", - "sha256:49f90f147883a0c3778fd29d3eb169d56416f25758d0f66775db9184debc8010", - "sha256:571b5a758baf1cb6a04233fb23d6cf1ca60b31f9f641b1700bfaab1194020555", - "sha256:5ac381e8b1259925287ccc5a87d9cf6322a2dc88ae28a97fe3e196385288413f", - "sha256:6153db744a743c0c8c91b8e3b9d40e0b13a5d31dbf8a12748c6d9bfd3ddc01ad", - "sha256:6fd63afd14a16f5d6b408f623cc2142917a1f92855f0df997e09a49f0341be8a", - "sha256:70acbcaba2a638923c2d337e0edea210505708d7859b87c2bd81e8f9902ae826", - "sha256:70b1594d56ed32d56ed21a7fbb2a5c6fd7446cdb7b21e749c9791eac3a64d9e4", - "sha256:76638865c83b1bb33bcac2a61ce4d13c17dba2204969dedb9ab60ef62bede686", - "sha256:7b2ec162c87fc496aa568258ac88631a2ce0acfe681a9af40842fc55deaedc99", - "sha256:7cee2cef07c8d76894ebefc54e4bb707dfc7f258ad155bd61d87f6cd487a70ff", - "sha256:7d16d4498f8b374fc625c4037742fbdd7f9ac383fd50b06f4df00c81ef60e829", - "sha256:b50bc1780681b127e28f0075dfb81d6135c3a293e0c1d0211133c75e2179b6c0", - "sha256:bd0582f831ad5bcad6ca001deba4568573a4675437db17c4031939156ff339fa", - "sha256:cfd40d8a4b59f7567620410f966bb1f32dc555b2b19f82a91b147fac296f645c", - "sha256:e3ae410089de680e8f84c68b755b42bc42c0ceb8c03dbea88a5099747091d38e", - "sha256:e9046e559c299b395b39ac7dbf16005308821c2f24a63cae2ab173bd6aa11616", - "sha256:ef6be704ae2bc8ad0ebc5cb850ee9139493b0fc4e81abcc240fb392a63ebc808", - "sha256:f8dc19d92896558f9c4317ee365729ead9d7bbcf2052a9a19a3ef17abbb8ac5b" + "sha256:047d9473cf68af50ac85f8ee5d5f21a60f849bc17d348da7fc85711287a75031", + "sha256:0f66dc6c8a3cc319561a633b6aa82c44107f12594643efa37210d8c924fc1c71", + "sha256:12c9169c4e8fe0a7329e8658c7e488001f6b4c8e88740e76292c2b857af2e94c", + "sha256:248cffc168896982f125f5c13e9317c059f74fffdb4152893339f3be62a01340", + "sha256:27faf0552bf8c260a5cee21a76e031acaea68babb64daf7e8f2e2540745082aa", + "sha256:285edafad9bc60d96978ed24d77cdc0b91dace88e5da8c548ba5937c425bca8b", + "sha256:384b12c9aa8ef95558abdcb50aada56d74bc7cc131dd62d28c2d0e4d3aadd573", + "sha256:38950b3a707f6cef09cd3cbb142474357ad1a985ceb44d921bdf7b4647b3e13e", + "sha256:4aad1b88933fd6dc2846552b89ad0c74ddbba2f0884e2c162aa368374bf5abab", + "sha256:4ac6148008c169603070c092e81f88738f1a0c511e07bd2bb0f9ef542d375da9", + "sha256:4deb1d2a45861ae6f0b12ea0a786a03d19d29edcc7e05775b85ec2877cb54c5e", + "sha256:59aa2c124df72cc75ed72c8d6005c442d4685691a30c55321e00ed915ad1a291", + "sha256:5a47d2123a9ec86660fe0e8d0ebf0aa6bc6a17edc63f338b73ea20ba11713f12", + "sha256:5cc901c2ab9409b4b7ac7b5bcc3e86ac14548627062463da0af3b6b7c555a871", + "sha256:6c1db03e8dff7b9f955a0fb9907eb9ca5da75b5ce056c0c93d33100a35050281", + "sha256:7ce80c0a65a6ea90ef9c1f63c8593fcd2929448613fc8da0adf3e6bfad669d08", + "sha256:809c19241c14433c5d6135e1b6c72da4e3b56d5c865ad5736ab99af8896b8f41", + "sha256:83792cb4e0b5af480588601467c0764242b9a483caea71ef12d22a0d0d6bdce2", + "sha256:846fa202bd7ee0f6215c897a1d33238ef071b50766339186687bd9b7a6d26ac5", + "sha256:9f5529fc02009f96ba95bea48870173426879dc19eec49ca8e08cd63ecd82ddb", + "sha256:a423c2ea001c6265ed28700df056f75e26215fd28c001e93ef4380b0f05f9547", + "sha256:ac4428094b42907aba5879c7c000d01c8278d451a3b7cccd2103e21f6397ea75", + "sha256:b1ae48d87f10d1384e5beecd169c77502fcc04a2c00a4c02b85f0a94b419e5f9", + "sha256:bf4e972a88f8841d8fdc6db1a75e0f8d763e66e3754b03006cbc3854d89f1cb1", + "sha256:c6414f6aad598364aaf81068cabb077894eb88fed99c6a65e6e8217bab62ae7a", + "sha256:c710fcb7ee32f67baf25aa9ffede4795fd5d93b163ce95fdc724383e38c9df96", + "sha256:c7be4b8a09852291c3c48d3c25d1b876d2494a0a674980089ac9d5e0d78bd132", + "sha256:c9e5ffb910b14f090ac9c38599063e354887a5f6d7e6d26795e916b4514f2c1a", + "sha256:e0697b826da6c2472bb6488db4c0a7fa8af0d52fa08833ceb3681358914b14e5", + "sha256:e9a3edd5f714229d41057d56ac0f39ad9bdba6767e8c888c951869f0bdd129b0" ], "index": "pypi", - "version": "==6.1.0" + "version": "==6.2.1" }, "psycopg2": { "hashes": [ - "sha256:128d0fa910ada0157bba1cb74a9c5f92bb8a1dca77cf91a31eb274d1f889e001", - "sha256:227fd46cf9b7255f07687e5bde454d7d67ae39ca77e170097cdef8ebfc30c323", - "sha256:2315e7f104681d498ccf6fd70b0dba5bce65d60ac92171492bfe228e21dcc242", - "sha256:4b5417dcd2999db0f5a891d54717cfaee33acc64f4772c4bc574d4ff95ed9d80", - "sha256:640113ddc943522aaf71294e3f2d24013b0edd659b7820621492c9ebd3a2fb0b", - "sha256:897a6e838319b4bf648a574afb6cabcb17d0488f8c7195100d48d872419f4457", - "sha256:8dceca81409898c870e011c71179454962dec152a1a6b86a347f4be74b16d864", - "sha256:b1b8e41da09a0c3ef0b3d4bb72da0dde2abebe583c1e8462973233fd5ad0235f", - "sha256:cb407fccc12fc29dc331f2b934913405fa49b9b75af4f3a72d0f50f57ad2ca23", - "sha256:d3a27550a8185e53b244ad7e79e307594b92fede8617d80200a8cce1fba2c60f", - "sha256:f0e6b697a975d9d3ccd04135316c947dd82d841067c7800ccf622a8717e98df1" + "sha256:4212ca404c4445dc5746c0d68db27d2cbfb87b523fe233dc84ecd24062e35677", + "sha256:47fc642bf6f427805daf52d6e52619fe0637648fe27017062d898f3bf891419d", + "sha256:72772181d9bad1fa349792a1e7384dde56742c14af2b9986013eb94a240f005b", + "sha256:8396be6e5ff844282d4d49b81631772f80dabae5658d432202faf101f5283b7c", + "sha256:893c11064b347b24ecdd277a094413e1954f8a4e8cdaf7ffbe7ca3db87c103f0", + "sha256:92a07dfd4d7c325dd177548c4134052d4842222833576c8391aab6f74038fc3f", + "sha256:965c4c93e33e6984d8031f74e51227bd755376a9df6993774fd5b6fb3288b1f4", + "sha256:9ab75e0b2820880ae24b7136c4d230383e07db014456a476d096591172569c38", + "sha256:b0845e3bdd4aa18dc2f9b6fb78fbd3d9d371ad167fd6d1b7ad01c0a6cdad4fc6", + "sha256:dca2d7203f0dfce8ea4b3efd668f8ea65cd2b35112638e488a4c12594015f67b", + "sha256:ed686e5926929887e2c7ae0a700e32c6129abb798b4ad2b846e933de21508151", + "sha256:ef6df7e14698e79c59c7ee7cf94cd62e5b869db369ed4b1b8f7b729ea825712a", + "sha256:f898e5cc0a662a9e12bde6f931263a1bbd350cfb18e1d5336a12927851825bb6" + ], + "index": "pypi", + "version": "==2.8.4" + }, + "pyscss": { + "hashes": [ + "sha256:14a25c33c221a66bb1f000a6a067f376528d3df2f9333cee9c95709a9280cdb0" ], "index": "pypi", - "version": "==2.8.3" + "version": "==1.3.5" }, "python-dateutil": { "hashes": [ - "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", - "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e" + "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", + "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" ], - "version": "==2.8.0" + "version": "==2.8.1" }, "python-editor": { "hashes": [ @@ -173,17 +236,22 @@ }, "six": { "hashes": [ - "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", - "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", + "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66" ], - "version": "==1.12.0" + "version": "==1.13.0" }, "sqlalchemy": { "hashes": [ - "sha256:2f8ff566a4d3a92246d367f2e9cd6ed3edeef670dcd6dda6dfdc9efed88bcd80" + "sha256:afa5541e9dea8ad0014251bc9d56171ca3d8b130c9627c6cb3681cff30be3f8a" ], - "index": "pypi", - "version": "==1.3.8" + "version": "==1.3.11" + }, + "webassets": { + "hashes": [ + "sha256:e7d9c8887343123fd5b32309b33167428cb1318cdda97ece12d0907fd69d38db" + ], + "version": "==0.12.1" }, "werkzeug": { "hashes": [ @@ -191,15 +259,22 @@ "sha256:e5f4a1f98b52b18a93da705a7458e55afb26f32bff83ff5d19189f92462d65c4" ], "version": "==0.16.0" + }, + "wtforms": { + "hashes": [ + "sha256:0cdbac3e7f6878086c334aa25dc5a33869a3954e9d1e015130d65a69309b3b61", + "sha256:e3ee092c827582c50877cdbd49e9ce6d2c5c1f6561f849b3b068c1b8029626f1" + ], + "version": "==2.2.1" } }, "develop": { "astroid": { "hashes": [ - "sha256:6560e1e1749f68c64a4b5dee4e091fce798d2f0d84ebe638cf0e0585a343acf4", - "sha256:b65db1bbaac9f9f4d190199bb8680af6f6f84fd3769a5ea883df8a91fe68b4c4" + "sha256:71ea07f44df9568a75d0f354c49143a4575d90645e9fead6dfb52c26a85ed13a", + "sha256:840947ebfa8b58f318d42301cf8c0a20fd794a33b61cc4638e28e9e61ba32f42" ], - "version": "==2.2.5" + "version": "==2.3.3" }, "click": { "hashes": [ @@ -218,11 +293,11 @@ }, "flask-sqlalchemy": { "hashes": [ - "sha256:0c9609b0d72871c540a7945ea559c8fdf5455192d2db67219509aed680a3d45a", - "sha256:8631bbea987bc3eb0f72b1f691d47bd37ceb795e73b59ab48586d76d75a7c605" + "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", + "sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d" ], "index": "pypi", - "version": "==2.4.0" + "version": "==2.4.1" }, "isort": { "hashes": [ @@ -240,33 +315,36 @@ }, "jinja2": { "hashes": [ - "sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", - "sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b" + "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", + "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de" ], - "version": "==2.10.1" + "version": "==2.10.3" }, "lazy-object-proxy": { "hashes": [ - "sha256:02b260c8deb80db09325b99edf62ae344ce9bc64d68b7a634410b8e9a568edbf", - "sha256:18f9c401083a4ba6e162355873f906315332ea7035803d0fd8166051e3d402e3", - "sha256:1f2c6209a8917c525c1e2b55a716135ca4658a3042b5122d4e3413a4030c26ce", - "sha256:2f06d97f0ca0f414f6b707c974aaf8829c2292c1c497642f63824119d770226f", - "sha256:616c94f8176808f4018b39f9638080ed86f96b55370b5a9463b2ee5c926f6c5f", - "sha256:63b91e30ef47ef68a30f0c3c278fbfe9822319c15f34b7538a829515b84ca2a0", - "sha256:77b454f03860b844f758c5d5c6e5f18d27de899a3db367f4af06bec2e6013a8e", - "sha256:83fe27ba321e4cfac466178606147d3c0aa18e8087507caec78ed5a966a64905", - "sha256:84742532d39f72df959d237912344d8a1764c2d03fe58beba96a87bfa11a76d8", - "sha256:874ebf3caaf55a020aeb08acead813baf5a305927a71ce88c9377970fe7ad3c2", - "sha256:9f5caf2c7436d44f3cec97c2fa7791f8a675170badbfa86e1992ca1b84c37009", - "sha256:a0c8758d01fcdfe7ae8e4b4017b13552efa7f1197dd7358dc9da0576f9d0328a", - "sha256:a4def978d9d28cda2d960c279318d46b327632686d82b4917516c36d4c274512", - "sha256:ad4f4be843dace866af5fc142509e9b9817ca0c59342fdb176ab6ad552c927f5", - "sha256:ae33dd198f772f714420c5ab698ff05ff900150486c648d29951e9c70694338e", - "sha256:b4a2b782b8a8c5522ad35c93e04d60e2ba7f7dcb9271ec8e8c3e08239be6c7b4", - "sha256:c462eb33f6abca3b34cdedbe84d761f31a60b814e173b98ede3c81bb48967c4f", - "sha256:fd135b8d35dfdcdb984828c84d695937e58cc5f49e1c854eb311c4d6aa03f4f1" - ], - "version": "==1.4.2" + "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d", + "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449", + "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08", + "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a", + "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50", + "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd", + "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239", + "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb", + "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea", + "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e", + "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156", + "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142", + "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442", + "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62", + "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db", + "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531", + "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383", + "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a", + "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357", + "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", + "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" + ], + "version": "==1.4.3" }, "markupsafe": { "hashes": [ @@ -310,11 +388,11 @@ }, "pylint": { "hashes": [ - "sha256:5d77031694a5fb97ea95e828c8d10fc770a1df6eb3906067aaed42201a8a6a09", - "sha256:723e3db49555abaf9bf79dc474c6b9e2935ad82230b10c1138a71ea41ac0fff1" + "sha256:3db5468ad013380e987410a8d6956226963aed94ecb5f9d3a28acca6d9ac36cd", + "sha256:886e6afc935ea2590b462664b161ca9a5e40168ea99e5300935f6591ad467df4" ], "index": "pypi", - "version": "==2.3.1" + "version": "==2.4.4" }, "pylint-flask-sqlalchemy": { "hashes": [ @@ -335,38 +413,16 @@ }, "six": { "hashes": [ - "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", - "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", + "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66" ], - "version": "==1.12.0" + "version": "==1.13.0" }, "sqlalchemy": { "hashes": [ - "sha256:2f8ff566a4d3a92246d367f2e9cd6ed3edeef670dcd6dda6dfdc9efed88bcd80" + "sha256:afa5541e9dea8ad0014251bc9d56171ca3d8b130c9627c6cb3681cff30be3f8a" ], - "index": "pypi", - "version": "==1.3.8" - }, - "typed-ast": { - "hashes": [ - "sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e", - "sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e", - "sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0", - "sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c", - "sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631", - "sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4", - "sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34", - "sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b", - "sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a", - "sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233", - "sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1", - "sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36", - "sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d", - "sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a", - "sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12" - ], - "markers": "implementation_name == 'cpython'", - "version": "==1.4.0" + "version": "==1.3.11" }, "werkzeug": { "hashes": [ diff --git a/yadc/__init__.py b/yadc/__init__.py index dd780b8..ea1ef50 100644 --- a/yadc/__init__.py +++ b/yadc/__init__.py @@ -3,16 +3,21 @@ import os from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate +from flask_login import LoginManager +from flask_assets import Environment as AssetsEnvironment, Bundle as AssetsBundle db = SQLAlchemy() migrate = Migrate() +login = LoginManager() +assets = AssetsEnvironment() def create_app(): app = Flask(__name__) app.config.from_mapping( - #SQLALCHEMY_DATABASE_URI='sqlite:///{}'.format(os.path.join(app.instance_path, 'yadc.db')) - SQLALCHEMY_DATABASE_URI='postgresql://{}:{}@{}:{}/{}'.format('yadc', 'omegalul', 'localhost', 5432, 'yadc') + SECRET_KEY='dev', + #SQLALCHEMY_DATABASE_URI='sqlite:///{}'.format(os.path.join(app.instance_path, 'yadc.db')), + SQLALCHEMY_DATABASE_URI='postgresql://{}:{}@{}:{}/{}'.format('yadc', 'omegalul', 'localhost', 5432, 'yadc'), ) try: @@ -22,8 +27,17 @@ def create_app(): db.init_app(app) migrate.init_app(app, db) + login.init_app(app) + assets.init_app(app) + from yadc import main, auth + app.register_blueprint(main.bp) + app.register_blueprint(auth.bp) + login.login_view = 'auth.login' + #assets.url = app.static_url_path + scss = AssetsBundle('default.scss', filters='libsass', output='all.css') + assets.register('scss_all', scss) from yadc import models diff --git a/yadc/auth.py b/yadc/auth.py new file mode 100644 index 0000000..16875d5 --- /dev/null +++ b/yadc/auth.py @@ -0,0 +1,40 @@ +from flask import request +from flask_wtf import FlaskForm +from wtforms import StringField, PasswordField, BooleanField, SubmitField +from wtforms.validators import DataRequired +from flask_login import current_user, login_user, logout_user +from werkzeug.urls import url_parse + +from yadc.models import User + +class LoginForm(FlaskForm): + username = StringField('Username', validators=[DataRequired()]) + password = PasswordField('Password', validators=[DataRequired()]) + remember_me = BooleanField('Remember me') + submit = SubmitField('Log In') + +from flask import Blueprint, render_template, flash, redirect, url_for + +bp = Blueprint('auth', __name__) + +@bp.route('/login', methods=['GET', 'POST']) +def login(): + form = LoginForm() + if form.validate_on_submit(): + #flash('Login requested for user {}, remember_me={}'.format(form.username.data, form.remember_me.data)) + user = User.query.filter_by(username=form.username.data).first() + if user is None or not user.check_password(form.password.data): + flash('Invalid username or password.') + return redirect(url_for('auth.login')) + login_user(user, remember=form.remember_me.data) + nextpg = request.args.get('next') + if not nextpg and url_parse(nextpg).netloc != '': + nextpg = url_for('main.index') + #flash('Logged in as {}'.format(user.username)) + return redirect(nextpg) + return render_template('login.html', form=form) + +@bp.route('/logout') +def logout(): + logout_user() + return redirect(url_for('main.index')) diff --git a/yadc/main.py b/yadc/main.py new file mode 100644 index 0000000..a9110a4 --- /dev/null +++ b/yadc/main.py @@ -0,0 +1,40 @@ +from flask import Blueprint, render_template, flash, redirect +from flask_login import login_required + +bp = Blueprint('main', __name__) + +@bp.route('/index') +def index(): + return render_template('index.html') + +@bp.route('/humm') +@login_required +def humm(): + return render_template('index.html') + +@bp.route('/') +@bp.route('/post') +def post(): + return render_template('index.html') + pass + +@bp.route('/post/show/') +def post_show(id): + return render_template('post.html') + pass + +@bp.route('/post/new') +@bp.route('/post/create') +@bp.route('/post/upload') +@login_required +def post_upload(): + pass + +@bp.route('/user/show/') +def user_profile(username): + pass + +@bp.route('/user/settings') +@login_required +def user_settings(): + pass \ No newline at end of file diff --git a/yadc/models.py b/yadc/models.py index 51ce01d..63f4ea5 100644 --- a/yadc/models.py +++ b/yadc/models.py @@ -1,7 +1,10 @@ import enum from datetime import datetime -from yadc import db +from flask_login import UserMixin +from werkzeug.security import check_password_hash, generate_password_hash + +from yadc import db, login class OP_LEVEL(enum.Enum): @@ -32,12 +35,11 @@ class TAG_CATEGORY(enum.Enum): character = 4 copyright = 5 -class User(db.Model): +class User(UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(128), unique=True, nullable=False) email = db.Column(db.String(256), unique=True) pass_hash = db.Column(db.String(128)) - pass_salt = db.Column(db.String(128)) op_level = db.Column(db.Enum(OP_LEVEL), default=OP_LEVEL.user, nullable=False) created = db.Column(db.DateTime, default=datetime.now()) updated = db.Column(db.DateTime, default=datetime.now()) @@ -46,6 +48,19 @@ class User(db.Model): #authored_posts = db.relationship('Post', back_populates='author') #approved_posts = db.relationship('Post', back_populates='approver') + def __repr__(self): + return ''.format(self.username) + + def create_password(self, password): + self.pass_hash = generate_password_hash(password, salt_length=16) + + def check_password(self, password): + return check_password_hash(self.pass_hash, password) + +@login.user_loader +def load_user(id): + return User.query.get(int(id)) + post_tags = db.Table('post_tags', db.metadata, db.Column('post_id', db.Integer, db.ForeignKey('post.id')), db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')) @@ -92,5 +107,3 @@ class Comment(db.Model): user = db.relationship('User', backref=db.backref('comments', lazy=True)) # THINK ABOUT LAZY LOADING - - \ No newline at end of file diff --git a/yadc/static/_include-media.scss b/yadc/static/_include-media.scss new file mode 100644 index 0000000..2328eff --- /dev/null +++ b/yadc/static/_include-media.scss @@ -0,0 +1,567 @@ +@charset "UTF-8"; + +// _ _ _ _ _ +// (_) | | | | | (_) +// _ _ __ ___| |_ _ __| | ___ _ __ ___ ___ __| |_ __ _ +// | | '_ \ / __| | | | |/ _` |/ _ \ | '_ ` _ \ / _ \/ _` | |/ _` | +// | | | | | (__| | |_| | (_| | __/ | | | | | | __/ (_| | | (_| | +// |_|_| |_|\___|_|\__,_|\__,_|\___| |_| |_| |_|\___|\__,_|_|\__,_| +// +// Simple, elegant and maintainable media queries in Sass +// v1.4.9 +// +// http://include-media.com +// +// Authors: Eduardo Boucas (@eduardoboucas) +// Hugo Giraudel (@hugogiraudel) +// +// This project is licensed under the terms of the MIT license + + +//// +/// include-media library public configuration +/// @author Eduardo Boucas +/// @access public +//// + + +/// +/// Creates a list of global breakpoints +/// +/// @example scss - Creates a single breakpoint with the label `phone` +/// $breakpoints: ('phone': 320px); +/// +$breakpoints: ( + 'phone': 320px, + 'tablet': 768px, + 'desktop': 1024px +) !default; + + +/// +/// Creates a list of static expressions or media types +/// +/// @example scss - Creates a single media type (screen) +/// $media-expressions: ('screen': 'screen'); +/// +/// @example scss - Creates a static expression with logical disjunction (OR operator) +/// $media-expressions: ( +/// 'retina2x': '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)' +/// ); +/// +$media-expressions: ( + 'screen': 'screen', + 'print': 'print', + 'handheld': 'handheld', + 'landscape': '(orientation: landscape)', + 'portrait': '(orientation: portrait)', + 'retina2x': '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi), (min-resolution: 2dppx)', + 'retina3x': '(-webkit-min-device-pixel-ratio: 3), (min-resolution: 350dpi), (min-resolution: 3dppx)' +) !default; + + +/// +/// Defines a number to be added or subtracted from each unit when declaring breakpoints with exclusive intervals +/// +/// @example scss - Interval for pixels is defined as `1` by default +/// @include media('>128px') {} +/// +/// /* Generates: */ +/// @media (min-width: 129px) {} +/// +/// @example scss - Interval for ems is defined as `0.01` by default +/// @include media('>20em') {} +/// +/// /* Generates: */ +/// @media (min-width: 20.01em) {} +/// +/// @example scss - Interval for rems is defined as `0.1` by default, to be used with `font-size: 62.5%;` +/// @include media('>2.0rem') {} +/// +/// /* Generates: */ +/// @media (min-width: 2.1rem) {} +/// +$unit-intervals: ( + 'px': 1, + 'em': 0.01, + 'rem': 0.1, + '': 0 +) !default; + +/// +/// Defines whether support for media queries is available, useful for creating separate stylesheets +/// for browsers that don't support media queries. +/// +/// @example scss - Disables support for media queries +/// $im-media-support: false; +/// @include media('>=tablet') { +/// .foo { +/// color: tomato; +/// } +/// } +/// +/// /* Generates: */ +/// .foo { +/// color: tomato; +/// } +/// +$im-media-support: true !default; + +/// +/// Selects which breakpoint to emulate when support for media queries is disabled. Media queries that start at or +/// intercept the breakpoint will be displayed, any others will be ignored. +/// +/// @example scss - This media query will show because it intercepts the static breakpoint +/// $im-media-support: false; +/// $im-no-media-breakpoint: 'desktop'; +/// @include media('>=tablet') { +/// .foo { +/// color: tomato; +/// } +/// } +/// +/// /* Generates: */ +/// .foo { +/// color: tomato; +/// } +/// +/// @example scss - This media query will NOT show because it does not intercept the desktop breakpoint +/// $im-media-support: false; +/// $im-no-media-breakpoint: 'tablet'; +/// @include media('>=desktop') { +/// .foo { +/// color: tomato; +/// } +/// } +/// +/// /* No output */ +/// +$im-no-media-breakpoint: 'desktop' !default; + +/// +/// Selects which media expressions are allowed in an expression for it to be used when media queries +/// are not supported. +/// +/// @example scss - This media query will show because it intercepts the static breakpoint and contains only accepted media expressions +/// $im-media-support: false; +/// $im-no-media-breakpoint: 'desktop'; +/// $im-no-media-expressions: ('screen'); +/// @include media('>=tablet', 'screen') { +/// .foo { +/// color: tomato; +/// } +/// } +/// +/// /* Generates: */ +/// .foo { +/// color: tomato; +/// } +/// +/// @example scss - This media query will NOT show because it intercepts the static breakpoint but contains a media expression that is not accepted +/// $im-media-support: false; +/// $im-no-media-breakpoint: 'desktop'; +/// $im-no-media-expressions: ('screen'); +/// @include media('>=tablet', 'retina2x') { +/// .foo { +/// color: tomato; +/// } +/// } +/// +/// /* No output */ +/// +$im-no-media-expressions: ('screen', 'portrait', 'landscape') !default; + +//// +/// Cross-engine logging engine +/// @author Hugo Giraudel +/// @access private +//// + + +/// +/// Log a message either with `@error` if supported +/// else with `@warn`, using `feature-exists('at-error')` +/// to detect support. +/// +/// @param {String} $message - Message to log +/// +@function im-log($message) { + @if feature-exists('at-error') { + @error $message; + } @else { + @warn $message; + $_: noop(); + } + + @return $message; +} + + +/// +/// Wrapper mixin for the log function so it can be used with a more friendly +/// API than `@if im-log('..') {}` or `$_: im-log('..')`. Basically, use the function +/// within functions because it is not possible to include a mixin in a function +/// and use the mixin everywhere else because it's much more elegant. +/// +/// @param {String} $message - Message to log +/// +@mixin log($message) { + @if im-log($message) {} +} + + +/// +/// Function with no `@return` called next to `@warn` in Sass 3.3 +/// to trigger a compiling error and stop the process. +/// +@function noop() {} + +/// +/// Determines whether a list of conditions is intercepted by the static breakpoint. +/// +/// @param {Arglist} $conditions - Media query conditions +/// +/// @return {Boolean} - Returns true if the conditions are intercepted by the static breakpoint +/// +@function im-intercepts-static-breakpoint($conditions...) { + $no-media-breakpoint-value: map-get($breakpoints, $im-no-media-breakpoint); + + @if not $no-media-breakpoint-value { + @if im-log('`#{$im-no-media-breakpoint}` is not a valid breakpoint.') {} + } + + @each $condition in $conditions { + @if not map-has-key($media-expressions, $condition) { + $operator: get-expression-operator($condition); + $prefix: get-expression-prefix($operator); + $value: get-expression-value($condition, $operator); + + @if ($prefix == 'max' and $value <= $no-media-breakpoint-value) or + ($prefix == 'min' and $value > $no-media-breakpoint-value) { + @return false; + } + } @else if not index($im-no-media-expressions, $condition) { + @return false; + } + } + + @return true; +} + +//// +/// Parsing engine +/// @author Hugo Giraudel +/// @access private +//// + + +/// +/// Get operator of an expression +/// +/// @param {String} $expression - Expression to extract operator from +/// +/// @return {String} - Any of `>=`, `>`, `<=`, `<`, `≥`, `≤` +/// +@function get-expression-operator($expression) { + @each $operator in ('>=', '>', '<=', '<', '≥', '≤') { + @if str-index($expression, $operator) { + @return $operator; + } + } + + // It is not possible to include a mixin inside a function, so we have to + // rely on the `im-log(..)` function rather than the `log(..)` mixin. Because + // functions cannot be called anywhere in Sass, we need to hack the call in + // a dummy variable, such as `$_`. If anybody ever raise a scoping issue with + // Sass 3.3, change this line in `@if im-log(..) {}` instead. + $_: im-log('No operator found in `#{$expression}`.'); +} + + +/// +/// Get dimension of an expression, based on a found operator +/// +/// @param {String} $expression - Expression to extract dimension from +/// @param {String} $operator - Operator from `$expression` +/// +/// @return {String} - `width` or `height` (or potentially anything else) +/// +@function get-expression-dimension($expression, $operator) { + $operator-index: str-index($expression, $operator); + $parsed-dimension: str-slice($expression, 0, $operator-index - 1); + $dimension: 'width'; + + @if str-length($parsed-dimension) > 0 { + $dimension: $parsed-dimension; + } + + @return $dimension; +} + + +/// +/// Get dimension prefix based on an operator +/// +/// @param {String} $operator - Operator +/// +/// @return {String} - `min` or `max` +/// +@function get-expression-prefix($operator) { + @return if(index(('<', '<=', '≤'), $operator), 'max', 'min'); +} + + +/// +/// Get value of an expression, based on a found operator +/// +/// @param {String} $expression - Expression to extract value from +/// @param {String} $operator - Operator from `$expression` +/// +/// @return {Number} - A numeric value +/// +@function get-expression-value($expression, $operator) { + $operator-index: str-index($expression, $operator); + $value: str-slice($expression, $operator-index + str-length($operator)); + + @if map-has-key($breakpoints, $value) { + $value: map-get($breakpoints, $value); + } @else { + $value: to-number($value); + } + + $interval: map-get($unit-intervals, unit($value)); + + @if not $interval { + // It is not possible to include a mixin inside a function, so we have to + // rely on the `im-log(..)` function rather than the `log(..)` mixin. Because + // functions cannot be called anywhere in Sass, we need to hack the call in + // a dummy variable, such as `$_`. If anybody ever raise a scoping issue with + // Sass 3.3, change this line in `@if im-log(..) {}` instead. + $_: im-log('Unknown unit `#{unit($value)}`.'); + } + + @if $operator == '>' { + $value: $value + $interval; + } @else if $operator == '<' { + $value: $value - $interval; + } + + @return $value; +} + + +/// +/// Parse an expression to return a valid media-query expression +/// +/// @param {String} $expression - Expression to parse +/// +/// @return {String} - Valid media query +/// +@function parse-expression($expression) { + // If it is part of $media-expressions, it has no operator + // then there is no need to go any further, just return the value + @if map-has-key($media-expressions, $expression) { + @return map-get($media-expressions, $expression); + } + + $operator: get-expression-operator($expression); + $dimension: get-expression-dimension($expression, $operator); + $prefix: get-expression-prefix($operator); + $value: get-expression-value($expression, $operator); + + @return '(#{$prefix}-#{$dimension}: #{$value})'; +} + +/// +/// Slice `$list` between `$start` and `$end` indexes +/// +/// @access private +/// +/// @param {List} $list - List to slice +/// @param {Number} $start [1] - Start index +/// @param {Number} $end [length($list)] - End index +/// +/// @return {List} Sliced list +/// +@function slice($list, $start: 1, $end: length($list)) { + @if length($list) < 1 or $start > $end { + @return (); + } + + $result: (); + + @for $i from $start through $end { + $result: append($result, nth($list, $i)); + } + + @return $result; +} + +//// +/// String to number converter +/// @author Hugo Giraudel +/// @access private +//// + + +/// +/// Casts a string into a number +/// +/// @param {String | Number} $value - Value to be parsed +/// +/// @return {Number} +/// +@function to-number($value) { + @if type-of($value) == 'number' { + @return $value; + } @else if type-of($value) != 'string' { + $_: im-log('Value for `to-number` should be a number or a string.'); + } + + $first-character: str-slice($value, 1, 1); + $result: 0; + $digits: 0; + $minus: ($first-character == '-'); + $numbers: ('0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9); + + // Remove +/- sign if present at first character + @if ($first-character == '+' or $first-character == '-') { + $value: str-slice($value, 2); + } + + @for $i from 1 through str-length($value) { + $character: str-slice($value, $i, $i); + + @if not (index(map-keys($numbers), $character) or $character == '.') { + @return to-length(if($minus, -$result, $result), str-slice($value, $i)) + } + + @if $character == '.' { + $digits: 1; + } @else if $digits == 0 { + $result: $result * 10 + map-get($numbers, $character); + } @else { + $digits: $digits * 10; + $result: $result + map-get($numbers, $character) / $digits; + } + } + + @return if($minus, -$result, $result); +} + + +/// +/// Add `$unit` to `$value` +/// +/// @param {Number} $value - Value to add unit to +/// @param {String} $unit - String representation of the unit +/// +/// @return {Number} - `$value` expressed in `$unit` +/// +@function to-length($value, $unit) { + $units: ('px': 1px, 'cm': 1cm, 'mm': 1mm, '%': 1%, 'ch': 1ch, 'pc': 1pc, 'in': 1in, 'em': 1em, 'rem': 1rem, 'pt': 1pt, 'ex': 1ex, 'vw': 1vw, 'vh': 1vh, 'vmin': 1vmin, 'vmax': 1vmax); + + @if not index(map-keys($units), $unit) { + $_: im-log('Invalid unit `#{$unit}`.'); + } + + @return $value * map-get($units, $unit); +} + +/// +/// This mixin aims at redefining the configuration just for the scope of +/// the call. It is helpful when having a component needing an extended +/// configuration such as custom breakpoints (referred to as tweakpoints) +/// for instance. +/// +/// @author Hugo Giraudel +/// +/// @param {Map} $tweakpoints [()] - Map of tweakpoints to be merged with `$breakpoints` +/// @param {Map} $tweak-media-expressions [()] - Map of tweaked media expressions to be merged with `$media-expression` +/// +/// @example scss - Extend the global breakpoints with a tweakpoint +/// @include media-context(('custom': 678px)) { +/// .foo { +/// @include media('>phone', '<=custom') { +/// // ... +/// } +/// } +/// } +/// +/// @example scss - Extend the global media expressions with a custom one +/// @include media-context($tweak-media-expressions: ('all': 'all')) { +/// .foo { +/// @include media('all', '>phone') { +/// // ... +/// } +/// } +/// } +/// +/// @example scss - Extend both configuration maps +/// @include media-context(('custom': 678px), ('all': 'all')) { +/// .foo { +/// @include media('all', '>phone', '<=custom') { +/// // ... +/// } +/// } +/// } +/// +@mixin media-context($tweakpoints: (), $tweak-media-expressions: ()) { + // Save global configuration + $global-breakpoints: $breakpoints; + $global-media-expressions: $media-expressions; + + // Update global configuration + $breakpoints: map-merge($breakpoints, $tweakpoints) !global; + $media-expressions: map-merge($media-expressions, $tweak-media-expressions) !global; + + @content; + + // Restore global configuration + $breakpoints: $global-breakpoints !global; + $media-expressions: $global-media-expressions !global; +} + +//// +/// include-media public exposed API +/// @author Eduardo Boucas +/// @access public +//// + + +/// +/// Generates a media query based on a list of conditions +/// +/// @param {Arglist} $conditions - Media query conditions +/// +/// @example scss - With a single set breakpoint +/// @include media('>phone') { } +/// +/// @example scss - With two set breakpoints +/// @include media('>phone', '<=tablet') { } +/// +/// @example scss - With custom values +/// @include media('>=358px', '<850px') { } +/// +/// @example scss - With set breakpoints with custom values +/// @include media('>desktop', '<=1350px') { } +/// +/// @example scss - With a static expression +/// @include media('retina2x') { } +/// +/// @example scss - Mixing everything +/// @include media('>=350px', ' 0) { + @media #{unquote(parse-expression(nth($conditions, 1)))} { + // Recursive call + @include media(slice($conditions, 2)...) { + @content; + } + } + } +} diff --git a/yadc/static/default.scss b/yadc/static/default.scss new file mode 100644 index 0000000..d86e0ad --- /dev/null +++ b/yadc/static/default.scss @@ -0,0 +1,356 @@ +@import "_include-media"; +$breakpoints: (tablet: 560px, desktop: 900px); + +* { + box-sizing: border-box; +} + +$bg-color: #222; //#20141f; +$text-color: #fff; +$a-color: #fff; +$ahover-color: #909090; + +$header-bg-color: #111; +$nav-bg-color: #222d;//#20141fd0; +$nav-bg-color_hover: #333;//#20141fa0; + +$desktop-width: 560px; + +@mixin mobile { + @media only screen and (max-width: #{$desktop-width - 1px}) { + @content; + } +} +@mixin desktop { + @media only screen and (min-width: #{$desktop-width}) { + @content; + } +} + +body { + margin: 0; + font-family: Verdana, Geneva, Tahoma, sans-serif; + //font-size: 14px; + + background-color: $bg-color; + color: $text-color; +} + +a { + color: $a-color; + text-decoration: none; +} +a:hover { + color: $ahover-color; + text-decoration: none; +} + +header { + display: flex; + align-items: baseline; + + padding: 0 10px; + + background-color: $header-bg-color; + + > a.logo { + font-size: 2em; + margin: 6px; + } + + // @include mobile { font-size: 1.3em; } + // @include desktop { font-size: 1.1em; } + + @include media(" nav { + flex-grow: 1; + + display: flex; + + > ._overlay { + display: none; + } + + @include media(" a, a#user-menu, .user_dropdown > a { + padding: 12px; + padding-left: 24px; + //text-align: center; + + &:hover, &:active { + background-color: $nav-bg-color_hover; + } + } + + .user { + &::before { + content: ""; + display: block; + border-top: 1px solid grey; + margin: 2px 12px; + } + #user-menu { + display: block; + + } + .user_dropdown { + display: flex; + flex-flow: column nowrap; + } + } + + > ._overlay { + display: block; + + position: fixed; + width: 100%; + height: 100%; + z-index: -1; + background-color: #0006; + + @at-root { + html.oh { overflow: hidden; } + } + } + } + @include media(">=tablet") { + margin: 0px 5px; + align-items: center; + + > * { + margin: 0 5px; + padding: 6px 0; + } + + > .user { + padding: 0; + margin-left: auto; + margin-right: 0; + + #user-menu { + display: block; + padding: 6px 10px; //replication of nav/* + } + + position: relative; + .user_dropdown { + //display: none; + opacity: .1; // BOI DAT HACK + height: 0; + width: 0; + overflow: hidden; + + a { + padding: 10px; + } + a:hover { + + background-color: $nav-bg-color_hover; + } + } + + &:hover > .user_dropdown { + display: flex; + flex-flow: column nowrap; + + position: absolute; + margin: 0; + top: 100%; + left: 0; + right: 0; + z-index: 10; + + background-color: $nav-bg-color; + + opacity: 1; // BOI DAT HACK + height: unset; + width: unset; + transition: .2s ease; + } + } + } + } + > #nav-menu { + display: none; + + @include media("=desktop") { + flex-flow: row nowrap; + + } + + > section.tag_panel { + flex-shrink: 0; + + + article.tags { + // @include media(">=desktop") { + // margin-left: 10px; + // margin-top: 10px; + // } + + padding: 10px; + + .tag_container { + display: flex; + + > span { + margin: 2px 2px; + padding: 6px 12px; + + //text-align: center; + border-radius: 4px; + background-color: #0005; + + > .fa-tag { + font-size: .9em; + margin-right: 2px; + } + + > .count { + // display: none; + font-size: .8em; + } + // > a { + // display: block; + // } + } + } + @include media("=desktop") { + .tag_container { + flex-flow: column nowrap; + align-items: start; + } + } + } + } + + overflow: hidden; + > section.content { + .posts { + display: flex; + flex-flow: row wrap; + // margin-left: -4px; + // margin-right: -4px; + // margin-left: 0; + // margin-right: 0; + // margin: 0; + // padding: 8px; + // margin: 0 8px; + // @include media(">=desktop") { + // margin-right: 8px; + // margin-top: 8px; + // } + + &::after { + content: ""; + flex: /*10000*/350 0 350px; + } + + > figure { + position: relative; + margin: 8px; + + + img { + display: block; + width: 100%; + height: 100%; + border-radius: 4px; + + transition: .2s ease; + } + &:hover img { + opacity: .7; + } + + } + } + + .pagin { + margin: 10px auto; + + display: flex; + flex-flow: row nowrap; + + justify-content: center; + + > a { + display: block; + + background-color: #0005; + padding: 8px 12px; + + margin: 0 2px; + } + } + } + +} + +footer { + padding: 10px; + + // background-color: $header-bg-color; + + text-align: center; +} \ No newline at end of file diff --git a/yadc/templates/index.html b/yadc/templates/index.html new file mode 100644 index 0000000..dcf75bb --- /dev/null +++ b/yadc/templates/index.html @@ -0,0 +1,254 @@ + + + + + + +{% assets "scss_all" %} + +{% endassets %} + + + YaDc + + +
+ + + +
+
+
+ +
+
+
+
+ + + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + 1 + 2 + 3 + 4 + 5 + +
+
+
+ +
+ Produced by KuxaBeast +
+ + + + \ No newline at end of file diff --git a/yadc/templates/post.html b/yadc/templates/post.html new file mode 100644 index 0000000..d0e781f --- /dev/null +++ b/yadc/templates/post.html @@ -0,0 +1,144 @@ + + + + + + +{% assets "scss_all" %} + +{% endassets %} + + + YaDc + + +
+ + + +
+
+
+ +
+
+ +
+
+ +
+ Produced by KuxaBeast +
+ + + + \ No newline at end of file