Add djlint (#1122)

* Add DJlint configuration

* Initial reformat for djlint

* Add template linting to CI

* Add explanation for HTML template checks in CONTRIBUTING.md
This commit is contained in:
Carlos Quintana 2022-06-29 11:28:26 +02:00 committed by GitHub
parent f6a7ee981a
commit cb7868bdca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
148 changed files with 4974 additions and 5283 deletions

View File

@ -77,6 +77,7 @@ jobs:
run: | run: |
poetry run black --check . poetry run black --check .
poetry run flake8 poetry run flake8
poetry run djlint --check templates
- name: Run db migration - name: Run db migration
run: | run: |

View File

@ -145,7 +145,11 @@ The code is also checked with `flake8`, make sure to run `flake8` before creatin
poetry run flake8 poetry run flake8
``` ```
Nice to have: as we haven't found a good enough HTML code formatter, please reformat any HTML code with PyCharm. For HTML templates, we use `djlint`. Before creating a pull request, please run
```bash
poetry run djlint --check templates
```
## Test sending email ## Test sending email

314
poetry.lock generated
View File

@ -295,7 +295,7 @@ six = ">=1.9"
[[package]] [[package]]
name = "colorama" name = "colorama"
version = "0.4.3" version = "0.4.5"
description = "Cross-platform colored terminal text." description = "Cross-platform colored terminal text."
category = "main" category = "main"
optional = false optional = false
@ -317,14 +317,14 @@ cron = ["capturer (>=2.4)"]
[[package]] [[package]]
name = "coverage" name = "coverage"
version = "6.4" version = "6.4.1"
description = "Code coverage measurement for Python" description = "Code coverage measurement for Python"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
[package.dependencies] [package.dependencies]
tomli = {version = "*", optional = true, markers = "python_version < \"3.11\" and extra == \"toml\""} tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
[package.extras] [package.extras]
toml = ["tomli"] toml = ["tomli"]
@ -386,6 +386,26 @@ category = "dev"
optional = false optional = false
python-versions = "*" python-versions = "*"
[[package]]
name = "djlint"
version = "0.7.3"
description = "HTML Template Linter and Formatter"
category = "dev"
optional = false
python-versions = ">=3.6.2,<4.0.0"
[package.dependencies]
click = ">=8.0.1,<9.0.0"
colorama = ">=0.4.4,<0.5.0"
pathspec = ">=0.9.0,<0.10.0"
PyYAML = ">=6.0,<7.0"
regex = ">=2022.1.18,<2023.0.0"
tomlkit = ">=0.8.0,<0.9.0"
tqdm = ">=4.62.2,<5.0.0"
[package.extras]
test = ["coverage (>=6.2,<7.0)", "pytest (>=6.2.5,<7.0.0)", "pytest-cov (>=3.0.0,<4.0.0)"]
[[package]] [[package]]
name = "dkimpy" name = "dkimpy"
version = "1.0.5" version = "1.0.5"
@ -1462,19 +1482,19 @@ python-versions = "*"
[[package]] [[package]]
name = "pyyaml" name = "pyyaml"
version = "5.4" version = "6.0"
description = "YAML parser and emitter for Python" description = "YAML parser and emitter for Python"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" python-versions = ">=3.6"
[[package]] [[package]]
name = "regex" name = "regex"
version = "2020.9.27" version = "2022.6.2"
description = "Alternative regular expression module, to replace re." description = "Alternative regular expression module, to replace re."
category = "main" category = "main"
optional = false optional = false
python-versions = "*" python-versions = ">=3.6"
[[package]] [[package]]
name = "requests" name = "requests"
@ -1717,6 +1737,31 @@ category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
[[package]]
name = "tomlkit"
version = "0.8.0"
description = "Style preserving TOML library"
category = "dev"
optional = false
python-versions = ">=3.6,<4.0"
[[package]]
name = "tqdm"
version = "4.64.0"
description = "Fast, Extensible Progress Meter"
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[package.extras]
dev = ["py-make (>=0.1.0)", "twine", "wheel"]
notebook = ["ipywidgets (>=6)"]
slack = ["slack-sdk"]
telegram = ["requests"]
[[package]] [[package]]
name = "traitlets" name = "traitlets"
version = "5.0.4" version = "5.0.4"
@ -1960,7 +2005,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.7" python-versions = "^3.7"
content-hash = "be129ac44e9114f1a25702b4d32c1caae617d892ed7f59d8e01d778fb3a2b53c" content-hash = "29d95a850f0a87a38aabb8f5eddba072316860dae26943fd690898ea9bac2b02"
[metadata.files] [metadata.files]
aiohttp = [ aiohttp = [
@ -2139,55 +2184,55 @@ coinbase-commerce = [
{file = "coinbase_commerce-1.0.1.tar.gz", hash = "sha256:5f6807655e9297210c1178dca62d1c1a997505368311c55c8ebe4f466344d966"}, {file = "coinbase_commerce-1.0.1.tar.gz", hash = "sha256:5f6807655e9297210c1178dca62d1c1a997505368311c55c8ebe4f466344d966"},
] ]
colorama = [ colorama = [
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
] ]
coloredlogs = [ coloredlogs = [
{file = "coloredlogs-14.0-py2.py3-none-any.whl", hash = "sha256:346f58aad6afd48444c2468618623638dadab76e4e70d5e10822676f2d32226a"}, {file = "coloredlogs-14.0-py2.py3-none-any.whl", hash = "sha256:346f58aad6afd48444c2468618623638dadab76e4e70d5e10822676f2d32226a"},
{file = "coloredlogs-14.0.tar.gz", hash = "sha256:a1fab193d2053aa6c0a97608c4342d031f1f93a3d1218432c59322441d31a505"}, {file = "coloredlogs-14.0.tar.gz", hash = "sha256:a1fab193d2053aa6c0a97608c4342d031f1f93a3d1218432c59322441d31a505"},
] ]
coverage = [ coverage = [
{file = "coverage-6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:50ed480b798febce113709846b11f5d5ed1e529c88d8ae92f707806c50297abf"}, {file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"},
{file = "coverage-6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:26f8f92699756cb7af2b30720de0c5bb8d028e923a95b6d0c891088025a1ac8f"}, {file = "coverage-6.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ce1b258493cbf8aec43e9b50d89982346b98e9ffdfaae8ae5793bc112fb0068"},
{file = "coverage-6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60c2147921da7f4d2d04f570e1838db32b95c5509d248f3fe6417e91437eaf41"}, {file = "coverage-6.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:83c4e737f60c6936460c5be330d296dd5b48b3963f48634c53b3f7deb0f34ec4"},
{file = "coverage-6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:750e13834b597eeb8ae6e72aa58d1d831b96beec5ad1d04479ae3772373a8088"}, {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84e65ef149028516c6d64461b95a8dbcfce95cfd5b9eb634320596173332ea84"},
{file = "coverage-6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af5b9ee0fc146e907aa0f5fb858c3b3da9199d78b7bb2c9973d95550bd40f701"}, {file = "coverage-6.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f69718750eaae75efe506406c490d6fc5a6161d047206cc63ce25527e8a3adad"},
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a022394996419142b33a0cf7274cb444c01d2bb123727c4bb0b9acabcb515dea"}, {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e57816f8ffe46b1df8f12e1b348f06d164fd5219beba7d9433ba79608ef011cc"},
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5a78cf2c43b13aa6b56003707c5203f28585944c277c1f3f109c7b041b16bd39"}, {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01c5615d13f3dd3aa8543afc069e5319cfa0c7d712f6e04b920431e5c564a749"},
{file = "coverage-6.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9229d074e097f21dfe0643d9d0140ee7433814b3f0fc3706b4abffd1e3038632"}, {file = "coverage-6.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75ab269400706fab15981fd4bd5080c56bd5cc07c3bccb86aab5e1d5a88dc8f4"},
{file = "coverage-6.4-cp310-cp310-win32.whl", hash = "sha256:fb45fe08e1abc64eb836d187b20a59172053999823f7f6ef4f18a819c44ba16f"}, {file = "coverage-6.4.1-cp310-cp310-win32.whl", hash = "sha256:a7f3049243783df2e6cc6deafc49ea123522b59f464831476d3d1448e30d72df"},
{file = "coverage-6.4-cp310-cp310-win_amd64.whl", hash = "sha256:3cfd07c5889ddb96a401449109a8b97a165be9d67077df6802f59708bfb07720"}, {file = "coverage-6.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2ddcac99b2d2aec413e36d7a429ae9ebcadf912946b13ffa88e7d4c9b712d6"},
{file = "coverage-6.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:03014a74023abaf5a591eeeaf1ac66a73d54eba178ff4cb1fa0c0a44aae70383"}, {file = "coverage-6.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb73e0011b8793c053bfa85e53129ba5f0250fdc0392c1591fd35d915ec75c46"},
{file = "coverage-6.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c82f2cd69c71698152e943f4a5a6b83a3ab1db73b88f6e769fabc86074c3b08"}, {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106c16dfe494de3193ec55cac9640dd039b66e196e4641fa8ac396181578b982"},
{file = "coverage-6.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b546cf2b1974ddc2cb222a109b37c6ed1778b9be7e6b0c0bc0cf0438d9e45a6"}, {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f4f3df85aa39da00fd3ec4b5abeb7407e82b68c7c5ad181308b0e2526da5d4"},
{file = "coverage-6.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc173f1ce9ffb16b299f51c9ce53f66a62f4d975abe5640e976904066f3c835d"}, {file = "coverage-6.4.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:961e2fb0680b4f5ad63234e0bf55dfb90d302740ae9c7ed0120677a94a1590cb"},
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c53ad261dfc8695062fc8811ac7c162bd6096a05a19f26097f411bdf5747aee7"}, {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cec3a0f75c8f1031825e19cd86ee787e87cf03e4fd2865c79c057092e69e3a3b"},
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:eef5292b60b6de753d6e7f2d128d5841c7915fb1e3321c3a1fe6acfe76c38052"}, {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:129cd05ba6f0d08a766d942a9ed4b29283aff7b2cccf5b7ce279d50796860bb3"},
{file = "coverage-6.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:543e172ce4c0de533fa892034cce260467b213c0ea8e39da2f65f9a477425211"}, {file = "coverage-6.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bf5601c33213d3cb19d17a796f8a14a9eaa5e87629a53979a5981e3e3ae166f6"},
{file = "coverage-6.4-cp37-cp37m-win32.whl", hash = "sha256:00c8544510f3c98476bbd58201ac2b150ffbcce46a8c3e4fb89ebf01998f806a"}, {file = "coverage-6.4.1-cp37-cp37m-win32.whl", hash = "sha256:269eaa2c20a13a5bf17558d4dc91a8d078c4fa1872f25303dddcbba3a813085e"},
{file = "coverage-6.4-cp37-cp37m-win_amd64.whl", hash = "sha256:b84ab65444dcc68d761e95d4d70f3cfd347ceca5a029f2ffec37d4f124f61311"}, {file = "coverage-6.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f02cbbf8119db68455b9d763f2f8737bb7db7e43720afa07d8eb1604e5c5ae28"},
{file = "coverage-6.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d548edacbf16a8276af13063a2b0669d58bbcfca7c55a255f84aac2870786a61"}, {file = "coverage-6.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ffa9297c3a453fba4717d06df579af42ab9a28022444cae7fa605af4df612d54"},
{file = "coverage-6.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:033ebec282793bd9eb988d0271c211e58442c31077976c19c442e24d827d356f"}, {file = "coverage-6.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:145f296d00441ca703a659e8f3eb48ae39fb083baba2d7ce4482fb2723e050d9"},
{file = "coverage-6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742fb8b43835078dd7496c3c25a1ec8d15351df49fb0037bffb4754291ef30ce"}, {file = "coverage-6.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d44996140af8b84284e5e7d398e589574b376fb4de8ccd28d82ad8e3bea13"},
{file = "coverage-6.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55fae115ef9f67934e9f1103c9ba826b4c690e4c5bcf94482b8b2398311bf9c"}, {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2bd9a6fc18aab8d2e18f89b7ff91c0f34ff4d5e0ba0b33e989b3cd4194c81fd9"},
{file = "coverage-6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd698341626f3c77784858427bad0cdd54a713115b423d22ac83a28303d1d95"}, {file = "coverage-6.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3384f2a3652cef289e38100f2d037956194a837221edd520a7ee5b42d00cc605"},
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:62d382f7d77eeeaff14b30516b17bcbe80f645f5cf02bb755baac376591c653c"}, {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9b3e07152b4563722be523e8cd0b209e0d1a373022cfbde395ebb6575bf6790d"},
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:016d7f5cf1c8c84f533a3c1f8f36126fbe00b2ec0ccca47cc5731c3723d327c6"}, {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1480ff858b4113db2718848d7b2d1b75bc79895a9c22e76a221b9d8d62496428"},
{file = "coverage-6.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:69432946f154c6add0e9ede03cc43b96e2ef2733110a77444823c053b1ff5166"}, {file = "coverage-6.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:865d69ae811a392f4d06bde506d531f6a28a00af36f5c8649684a9e5e4a85c83"},
{file = "coverage-6.4-cp38-cp38-win32.whl", hash = "sha256:83bd142cdec5e4a5c4ca1d4ff6fa807d28460f9db919f9f6a31babaaa8b88426"}, {file = "coverage-6.4.1-cp38-cp38-win32.whl", hash = "sha256:664a47ce62fe4bef9e2d2c430306e1428ecea207ffd68649e3b942fa8ea83b0b"},
{file = "coverage-6.4-cp38-cp38-win_amd64.whl", hash = "sha256:4002f9e8c1f286e986fe96ec58742b93484195defc01d5cc7809b8f7acb5ece3"}, {file = "coverage-6.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:26dff09fb0d82693ba9e6231248641d60ba606150d02ed45110f9ec26404ed1c"},
{file = "coverage-6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e4f52c272fdc82e7c65ff3f17a7179bc5f710ebc8ce8a5cadac81215e8326740"}, {file = "coverage-6.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9c80df769f5ec05ad21ea34be7458d1dc51ff1fb4b2219e77fe24edf462d6df"},
{file = "coverage-6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b5578efe4038be02d76c344007b13119b2b20acd009a88dde8adec2de4f630b5"}, {file = "coverage-6.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39ee53946bf009788108b4dd2894bf1349b4e0ca18c2016ffa7d26ce46b8f10d"},
{file = "coverage-6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8099ea680201c2221f8468c372198ceba9338a5fec0e940111962b03b3f716a"}, {file = "coverage-6.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5b66caa62922531059bc5ac04f836860412f7f88d38a476eda0a6f11d4724f4"},
{file = "coverage-6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a00441f5ea4504f5abbc047589d09e0dc33eb447dc45a1a527c8b74bfdd32c65"}, {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd180ed867e289964404051a958f7cccabdeed423f91a899829264bb7974d3d3"},
{file = "coverage-6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e76bd16f0e31bc2b07e0fb1379551fcd40daf8cdf7e24f31a29e442878a827c"}, {file = "coverage-6.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84631e81dd053e8a0d4967cedab6db94345f1c36107c71698f746cb2636c63e3"},
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8d2e80dd3438e93b19e1223a9850fa65425e77f2607a364b6fd134fcd52dc9df"}, {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8c08da0bd238f2970230c2a0d28ff0e99961598cb2e810245d7fc5afcf1254e8"},
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:341e9c2008c481c5c72d0e0dbf64980a4b2238631a7f9780b0fe2e95755fb018"}, {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d42c549a8f41dc103a8004b9f0c433e2086add8a719da00e246e17cbe4056f72"},
{file = "coverage-6.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:21e6686a95025927775ac501e74f5940cdf6fe052292f3a3f7349b0abae6d00f"}, {file = "coverage-6.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:309ce4a522ed5fca432af4ebe0f32b21d6d7ccbb0f5fcc99290e71feba67c264"},
{file = "coverage-6.4-cp39-cp39-win32.whl", hash = "sha256:968ed5407f9460bd5a591cefd1388cc00a8f5099de9e76234655ae48cfdbe2c3"}, {file = "coverage-6.4.1-cp39-cp39-win32.whl", hash = "sha256:fdb6f7bd51c2d1714cea40718f6149ad9be6a2ee7d93b19e9f00934c0f2a74d9"},
{file = "coverage-6.4-cp39-cp39-win_amd64.whl", hash = "sha256:e35217031e4b534b09f9b9a5841b9344a30a6357627761d4218818b865d45055"}, {file = "coverage-6.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:342d4aefd1c3e7f620a13f4fe563154d808b69cccef415415aece4c786665397"},
{file = "coverage-6.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:e637ae0b7b481905358624ef2e81d7fb0b1af55f5ff99f9ba05442a444b11e45"}, {file = "coverage-6.4.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:4803e7ccf93230accb928f3a68f00ffa80a88213af98ed338a57ad021ef06815"},
{file = "coverage-6.4.tar.gz", hash = "sha256:727dafd7f67a6e1cad808dc884bd9c5a2f6ef1f8f6d2f22b37b96cb0080d4f49"}, {file = "coverage-6.4.1.tar.gz", hash = "sha256:4321f075095a096e70aff1d002030ee612b65a205a0a0f5b815280d5dc58100c"},
] ]
crontab = [ crontab = [
{file = "crontab-0.22.8.tar.gz", hash = "sha256:1ac977fb1b8ba5b7b58e6f713cd7df36e61d7aee4c2b809abcf76adddd2deeaf"}, {file = "crontab-0.22.8.tar.gz", hash = "sha256:1ac977fb1b8ba5b7b58e6f713cd7df36e61d7aee4c2b809abcf76adddd2deeaf"},
@ -2228,6 +2273,10 @@ distlib = [
{file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
{file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"},
] ]
djlint = [
{file = "djlint-0.7.3-py3-none-any.whl", hash = "sha256:714ed457e022047149c8bff57d5be00ce30f8846b60e866791c66d27e7d11e7f"},
{file = "djlint-0.7.3.tar.gz", hash = "sha256:68aad9ddfef883cc9d9e0d177387b74840af5ca12dcce6e4629eb7075c97dc05"},
]
dkimpy = [ dkimpy = [
{file = "dkimpy-1.0.5.tar.gz", hash = "sha256:9a2420bf09af686736773153fca32a02ae11ecbe24b540c26104628959f91121"}, {file = "dkimpy-1.0.5.tar.gz", hash = "sha256:9a2420bf09af686736773153fca32a02ae11ecbe24b540c26104628959f91121"},
] ]
@ -2904,56 +2953,115 @@ pytz = [
{file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"}, {file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"},
] ]
pyyaml = [ pyyaml = [
{file = "PyYAML-5.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f7a21e3d99aa3095ef0553e7ceba36fb693998fbb1226f1392ce33681047465f"}, {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
{file = "PyYAML-5.4-cp27-cp27m-win32.whl", hash = "sha256:52bf0930903818e600ae6c2901f748bc4869c0c406056f679ab9614e5d21a166"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
{file = "PyYAML-5.4-cp27-cp27m-win_amd64.whl", hash = "sha256:a36a48a51e5471513a5aea920cdad84cbd56d70a5057cca3499a637496ea379c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
{file = "PyYAML-5.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:5e7ac4e0e79a53451dc2814f6876c2fa6f71452de1498bbe29c0b54b69a986f4"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
{file = "PyYAML-5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc552b6434b90d9dbed6a4f13339625dc466fd82597119897e9489c953acbc22"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
{file = "PyYAML-5.4-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0dc9f2eb2e3c97640928dec63fd8dc1dd91e6b6ed236bd5ac00332b99b5c2ff9"}, {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
{file = "PyYAML-5.4-cp36-cp36m-win32.whl", hash = "sha256:5a3f345acff76cad4aa9cb171ee76c590f37394186325d53d1aa25318b0d4a09"}, {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
{file = "PyYAML-5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:f3790156c606299ff499ec44db422f66f05a7363b39eb9d5b064f17bd7d7c47b"}, {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
{file = "PyYAML-5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:124fd7c7bc1e95b1eafc60825f2daf67c73ce7b33f1194731240d24b0d1bf628"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
{file = "PyYAML-5.4-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8b818b6c5a920cbe4203b5a6b14256f0e5244338244560da89b7b0f1313ea4b6"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
{file = "PyYAML-5.4-cp37-cp37m-win32.whl", hash = "sha256:737bd70e454a284d456aa1fa71a0b429dd527bcbf52c5c33f7c8eee81ac16b89"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
{file = "PyYAML-5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:7242790ab6c20316b8e7bb545be48d7ed36e26bbe279fd56f2c4a12510e60b4b"}, {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
{file = "PyYAML-5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cc547d3ead3754712223abb7b403f0a184e4c3eae18c9bb7fd15adef1597cc4b"}, {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
{file = "PyYAML-5.4-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8635d53223b1f561b081ff4adecb828fd484b8efffe542edcfdff471997f7c39"}, {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
{file = "PyYAML-5.4-cp38-cp38-win32.whl", hash = "sha256:26fcb33776857f4072601502d93e1a619f166c9c00befb52826e7b774efaa9db"}, {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
{file = "PyYAML-5.4-cp38-cp38-win_amd64.whl", hash = "sha256:b2243dd033fd02c01212ad5c601dafb44fbb293065f430b0d3dbf03f3254d615"}, {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
{file = "PyYAML-5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:31ba07c54ef4a897758563e3a0fcc60077698df10180abe4b8165d9895c00ebf"}, {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
{file = "PyYAML-5.4-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:02c78d77281d8f8d07a255e57abdbf43b02257f59f50cc6b636937d68efa5dd0"}, {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
{file = "PyYAML-5.4-cp39-cp39-win32.whl", hash = "sha256:fdc6b2cb4b19e431994f25a9160695cc59a4e861710cc6fc97161c5e845fc579"}, {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
{file = "PyYAML-5.4-cp39-cp39-win_amd64.whl", hash = "sha256:8bf38641b4713d77da19e91f8b5296b832e4db87338d6aeffe422d42f1ca896d"}, {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
{file = "PyYAML-5.4.tar.gz", hash = "sha256:3c49e39ac034fd64fd576d63bb4db53cda89b362768a67f07749d55f128ac18a"}, {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
{file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
{file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
{file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
{file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
{file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
{file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
{file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
{file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
] ]
regex = [ regex = [
{file = "regex-2020.9.27-cp27-cp27m-win32.whl", hash = "sha256:d23a18037313714fb3bb5a94434d3151ee4300bae631894b1ac08111abeaa4a3"}, {file = "regex-2022.6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:042d122f9fee3ceb6d7e3067d56557df697d1aad4ff5f64ecce4dc13a90a7c01"},
{file = "regex-2020.9.27-cp27-cp27m-win_amd64.whl", hash = "sha256:84e9407db1b2eb368b7ecc283121b5e592c9aaedbe8c78b1a2f1102eb2e21d19"}, {file = "regex-2022.6.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ffef4b30785dc2d1604dfb7cf9fca5dc27cd86d65f7c2a9ec34d6d3ae4565ec2"},
{file = "regex-2020.9.27-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5f18875ac23d9aa2f060838e8b79093e8bb2313dbaaa9f54c6d8e52a5df097be"}, {file = "regex-2022.6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0afa6a601acf3c0dc6de4e8d7d8bbce4e82f8542df746226cd35d4a6c15e9456"},
{file = "regex-2020.9.27-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ae91972f8ac958039920ef6e8769277c084971a142ce2b660691793ae44aae6b"}, {file = "regex-2022.6.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a11cbe8eb5fb332ae474895b5ead99392a4ea568bd2a258ab8df883e9c2bf92"},
{file = "regex-2020.9.27-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9a02d0ae31d35e1ec12a4ea4d4cca990800f66a917d0fb997b20fbc13f5321fc"}, {file = "regex-2022.6.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c1f62ee2ba880e221bc950651a1a4b0176083d70a066c83a50ef0cb9b178e12"},
{file = "regex-2020.9.27-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ebbe29186a3d9b0c591e71b7393f1ae08c83cb2d8e517d2a822b8f7ec99dfd8b"}, {file = "regex-2022.6.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aba3d13c77173e9bfed2c2cea7fc319f11c89a36fcec08755e8fb169cf3b0df"},
{file = "regex-2020.9.27-cp36-cp36m-win32.whl", hash = "sha256:4707f3695b34335afdfb09be3802c87fa0bc27030471dbc082f815f23688bc63"}, {file = "regex-2022.6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:249437f7f5b233792234aeeecb14b0aab1566280de42dfc97c26e6f718297d68"},
{file = "regex-2020.9.27-cp36-cp36m-win_amd64.whl", hash = "sha256:9bc13e0d20b97ffb07821aa3e113f9998e84994fe4d159ffa3d3a9d1b805043b"}, {file = "regex-2022.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:179410c79fa86ef318d58ace233f95b87b05a1db6dc493fa29404a43f4b215e2"},
{file = "regex-2020.9.27-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f1b3afc574a3db3b25c89161059d857bd4909a1269b0b3cb3c904677c8c4a3f7"}, {file = "regex-2022.6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5e201b1232d81ca1a7a22ab2f08e1eccad4e111579fd7f3bbf60b21ef4a16cea"},
{file = "regex-2020.9.27-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5533a959a1748a5c042a6da71fe9267a908e21eded7a4f373efd23a2cbdb0ecc"}, {file = "regex-2022.6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fdecb225d0f1d50d4b26ac423e0032e76d46a788b83b4e299a520717a47d968c"},
{file = "regex-2020.9.27-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:1fe0a41437bbd06063aa184c34804efa886bcc128222e9916310c92cd54c3b4c"}, {file = "regex-2022.6.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:be57f9c7b0b423c66c266a26ad143b2c5514997c05dd32ce7ca95c8b209c2288"},
{file = "regex-2020.9.27-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:c570f6fa14b9c4c8a4924aaad354652366577b4f98213cf76305067144f7b100"}, {file = "regex-2022.6.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ed657a07d8a47ef447224ea00478f1c7095065dfe70a89e7280e5f50a5725131"},
{file = "regex-2020.9.27-cp37-cp37m-win32.whl", hash = "sha256:eda4771e0ace7f67f58bc5b560e27fb20f32a148cbc993b0c3835970935c2707"}, {file = "regex-2022.6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:24908aefed23dd065b4a668c0b4ca04d56b7f09d8c8e89636cf6c24e64e67a1e"},
{file = "regex-2020.9.27-cp37-cp37m-win_amd64.whl", hash = "sha256:60b0e9e6dc45683e569ec37c55ac20c582973841927a85f2d8a7d20ee80216ab"}, {file = "regex-2022.6.2-cp310-cp310-win32.whl", hash = "sha256:775694cd0bb2c4accf2f1cdd007381b33ec8b59842736fe61bdbad45f2ac7427"},
{file = "regex-2020.9.27-cp38-cp38-manylinux1_i686.whl", hash = "sha256:088afc8c63e7bd187a3c70a94b9e50ab3f17e1d3f52a32750b5b77dbe99ef5ef"}, {file = "regex-2022.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:809bbbbbcf8258049b031d80932ba71627d2274029386f0452e9950bcfa2c6e8"},
{file = "regex-2020.9.27-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eaf548d117b6737df379fdd53bdde4f08870e66d7ea653e230477f071f861121"}, {file = "regex-2022.6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2b5d983eb0adf2049d41f95205bdc3de4e6cc2350e9c80d4409d3a75229de"},
{file = "regex-2020.9.27-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:41bb65f54bba392643557e617316d0d899ed5b4946dccee1cb6696152b29844b"}, {file = "regex-2022.6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f4c101746a8dac0401abefa716b357c546e61ea2e3d4a564a9db9eac57ccbce"},
{file = "regex-2020.9.27-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:8d69cef61fa50c8133382e61fd97439de1ae623fe943578e477e76a9d9471637"}, {file = "regex-2022.6.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:166ae7674d0a0e0f8044e7335ba86d0716c9d49465cff1b153f908e0470b8300"},
{file = "regex-2020.9.27-cp38-cp38-win32.whl", hash = "sha256:f2388013e68e750eaa16ccbea62d4130180c26abb1d8e5d584b9baf69672b30f"}, {file = "regex-2022.6.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5eac5d8a8ac9ccf00805d02a968a36f5c967db6c7d2b747ab9ed782b3b3a28b"},
{file = "regex-2020.9.27-cp38-cp38-win_amd64.whl", hash = "sha256:4318d56bccfe7d43e5addb272406ade7a2274da4b70eb15922a071c58ab0108c"}, {file = "regex-2022.6.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f57823f35b18d82b201c1b27ce4e55f88e79e81d9ca07b50ce625d33823e1439"},
{file = "regex-2020.9.27-cp39-cp39-manylinux1_i686.whl", hash = "sha256:84cada8effefe9a9f53f9b0d2ba9b7b6f5edf8d2155f9fdbe34616e06ececf81"}, {file = "regex-2022.6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4d42e3b7b23473729adbf76103e7df75f9167a5a80b1257ca30688352b4bb2dc"},
{file = "regex-2020.9.27-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:816064fc915796ea1f26966163f6845de5af78923dfcecf6551e095f00983650"}, {file = "regex-2022.6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2932e728bee0a634fe55ee54d598054a5a9ffe4cd2be21ba2b4b8e5f8064c2c"},
{file = "regex-2020.9.27-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:5d892a4f1c999834eaa3c32bc9e8b976c5825116cde553928c4c8e7e48ebda67"}, {file = "regex-2022.6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:17764683ea01c2b8f103d99ae9de2473a74340df13ce306c49a721f0b1f0eb9e"},
{file = "regex-2020.9.27-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c9443124c67b1515e4fe0bb0aa18df640965e1030f468a2a5dc2589b26d130ad"}, {file = "regex-2022.6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:2ac29b834100d2c171085ceba0d4a1e7046c434ddffc1434dbc7f9d59af1e945"},
{file = "regex-2020.9.27-cp39-cp39-win32.whl", hash = "sha256:49f23ebd5ac073765ecbcf046edc10d63dcab2f4ae2bce160982cb30df0c0302"}, {file = "regex-2022.6.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:f43522fb5d676c99282ca4e2d41e8e2388427c0cf703db6b4a66e49b10b699a8"},
{file = "regex-2020.9.27-cp39-cp39-win_amd64.whl", hash = "sha256:3d20024a70b97b4f9546696cbf2fd30bae5f42229fbddf8661261b1eaff0deb7"}, {file = "regex-2022.6.2-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:9faa01818dad9111dbf2af26c6e3c45140ccbd1192c3a0981f196255bf7ec5e6"},
{file = "regex-2020.9.27.tar.gz", hash = "sha256:a6f32aea4260dfe0e55dc9733ea162ea38f0ea86aa7d0f77b15beac5bf7b369d"}, {file = "regex-2022.6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:17443f99b8f255273731f915fdbfea4d78d809bb9c3aaf67b889039825d06515"},
{file = "regex-2022.6.2-cp36-cp36m-win32.whl", hash = "sha256:4a5449adef907919d4ce7a1eab2e27d0211d1b255bf0b8f5dd330ad8707e0fc3"},
{file = "regex-2022.6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:4d206703a96a39763b5b45cf42645776f5553768ea7f3c2c1a39a4f59cafd4ba"},
{file = "regex-2022.6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fcd7c432202bcb8b642c3f43d5bcafc5930d82fe5b2bf2c008162df258445c1d"},
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:186c5a4a4c40621f64d771038ede20fca6c61a9faa8178f9e305aaa0c2442a97"},
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:047b2d1323a51190c01b6604f49fe09682a5c85d3c1b2c8b67c1cd68419ce3c4"},
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:30637e7fa4acfed444525b1ab9683f714be617862820578c9fd4e944d4d9ad1f"},
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3adafe6f2c6d86dbf3313866b61180530ca4dcd0c264932dc8fa1ffb10871d58"},
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67ae3601edf86e15ebe40885e5bfdd6002d34879070be15cf18fc0d80ea24fed"},
{file = "regex-2022.6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:48dddddce0ea7e7c3e92c1e0c5a28c13ca4dc9cf7e996c706d00479652bff76c"},
{file = "regex-2022.6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:68e5c641645351eb9eb12c465876e76b53717f99e9b92aea7a2dd645a87aa7aa"},
{file = "regex-2022.6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8fd5f8ae42f789538bb634bdfd69b9aa357e76fdfd7ad720f32f8994c0d84f1e"},
{file = "regex-2022.6.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:71988a76fcb68cc091e901fddbcac0f9ad9a475da222c47d3cf8db0876cb5344"},
{file = "regex-2022.6.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:4b8838f70be3ce9e706df9d72f88a0aa7d4c1fea61488e06fdf292ccb70ad2be"},
{file = "regex-2022.6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:663dca677bd3d2e2b5b7d0329e9f24247e6f38f3b740dd9a778a8ef41a76af41"},
{file = "regex-2022.6.2-cp37-cp37m-win32.whl", hash = "sha256:24963f0b13cc63db336d8da2a533986419890d128c551baacd934c249d51a779"},
{file = "regex-2022.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:ceff75127f828dfe7ceb17b94113ec2df4df274c4cd5533bb299cb099a18a8ca"},
{file = "regex-2022.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a6f2698cfa8340dfe4c0597782776b393ba2274fe4c079900c7c74f68752705"},
{file = "regex-2022.6.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8a08ace913c4101f0dc0be605c108a3761842efd5f41a3005565ee5d169fb2b"},
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26dbe90b724efef7820c3cf4a0e5be7f130149f3d2762782e4e8ac2aea284a0b"},
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5f759a1726b995dc896e86f17f9c0582b54eb4ead00ed5ef0b5b22260eaf2d0"},
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1fc26bb3415e7aa7495c000a2c13bf08ce037775db98c1a3fac9ff04478b6930"},
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52684da32d9003367dc1a1c07e059b9bbaf135ad0764cd47d8ac3dba2df109bc"},
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c1264eb40a71cf2bff43d6694ab7254438ca19ef330175060262b3c8dd3931a"},
{file = "regex-2022.6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bc635ab319c9b515236bdf327530acda99be995f9d3b9f148ab1f60b2431e970"},
{file = "regex-2022.6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:27624b490b5d8880f25dac67e1e2ea93dfef5300b98c6755f585799230d6c746"},
{file = "regex-2022.6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:555f7596fd1f123f8c3a67974c01d6ef80b9769e04d660d6c1a7cc3e6cff7069"},
{file = "regex-2022.6.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:933e72fbe1829cbd59da2bc51ccd73d73162f087f88521a87a8ec9cb0cf10fa8"},
{file = "regex-2022.6.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:cff5c87e941292c97d11dc81bd20679f56a2830f0f0e32f75b8ed6e0eb40f704"},
{file = "regex-2022.6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c757f3a27b6345de13ef3ca956aa805d7734ce68023e84d0fc74e1f09ce66f7a"},
{file = "regex-2022.6.2-cp38-cp38-win32.whl", hash = "sha256:a58d21dd1a2d6b50ed091554ff85e448fce3fe33a4db8b55d0eba2ca957ed626"},
{file = "regex-2022.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:495a4165172848503303ed05c9d0409428f789acc27050fe2cf0a4549188a7d5"},
{file = "regex-2022.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1ab5cf7d09515548044e69d3a0ec77c63d7b9dfff4afc19653f638b992573126"},
{file = "regex-2022.6.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c1ea28f0ee6cbe4c0367c939b015d915aa9875f6e061ba1cf0796ca9a3010570"},
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3de1ecf26ce85521bf73897828b6d0687cc6cf271fb6ff32ac63d26b21f5e764"},
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa7c7044aabdad2329974be2246babcc21d3ede852b3971a90fd8c2056c20360"},
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53d69d77e9cfe468b000314dd656be85bb9e96de088a64f75fe128dfe1bf30dd"},
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c8d61883a38b1289fba9944a19a361875b5c0170b83cdcc95ea180247c1b7d3"},
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5429202bef174a3760690d912e3a80060b323199a61cef6c6c29b30ce09fd17"},
{file = "regex-2022.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e85b10280cf1e334a7c95629f6cbbfe30b815a4ea5f1e28d31f79eb92c2c3d93"},
{file = "regex-2022.6.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c400dfed4137f32127ea4063447006d7153c974c680bf0fb1b724cce9f8567fc"},
{file = "regex-2022.6.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7f648037c503985aed39f85088acab6f1eb6a0482d7c6c665a5712c9ad9eaefc"},
{file = "regex-2022.6.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e7b2ff451f6c305b516281ec45425dd423223c8063218c5310d6f72a0a7a517c"},
{file = "regex-2022.6.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:be456b4313a86be41706319c397c09d9fdd2e5cdfde208292a277b867e99e3d1"},
{file = "regex-2022.6.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c3db393b21b53d7e1d3f881b64c29d886cbfdd3df007e31de68b329edbab7d02"},
{file = "regex-2022.6.2-cp39-cp39-win32.whl", hash = "sha256:d70596f20a03cb5f935d6e4aad9170a490d88fc4633679bf00c652e9def4619e"},
{file = "regex-2022.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:3b9b6289e03dbe6a6096880d8ac166cb23c38b4896ad235edee789d4e8697152"},
{file = "regex-2022.6.2.tar.gz", hash = "sha256:f7b43acb2c46fb2cd506965b2d9cf4c5e64c9c612bac26c1187933c7296bf08c"},
] ]
requests = [ requests = [
{file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
@ -3135,6 +3243,14 @@ tomli = [
{file = "tomli-2.0.0-py3-none-any.whl", hash = "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"}, {file = "tomli-2.0.0-py3-none-any.whl", hash = "sha256:b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"},
{file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"}, {file = "tomli-2.0.0.tar.gz", hash = "sha256:c292c34f58502a1eb2bbb9f5bbc9a5ebc37bee10ffb8c2d6bbdfa8eb13cc14e1"},
] ]
tomlkit = [
{file = "tomlkit-0.8.0-py3-none-any.whl", hash = "sha256:b824e3466f1d475b2b5f1c392954c6cb7ea04d64354ff7300dc7c14257dc85db"},
{file = "tomlkit-0.8.0.tar.gz", hash = "sha256:29e84a855712dfe0e88a48f6d05c21118dbafb283bb2eed614d46f80deb8e9a1"},
]
tqdm = [
{file = "tqdm-4.64.0-py2.py3-none-any.whl", hash = "sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"},
{file = "tqdm-4.64.0.tar.gz", hash = "sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d"},
]
traitlets = [ traitlets = [
{file = "traitlets-5.0.4-py3-none-any.whl", hash = "sha256:9664ec0c526e48e7b47b7d14cd6b252efa03e0129011de0a9c1d70315d4309c3"}, {file = "traitlets-5.0.4-py3-none-any.whl", hash = "sha256:9664ec0c526e48e7b47b7d14cd6b252efa03e0129011de0a9c1d70315d4309c3"},
{file = "traitlets-5.0.4.tar.gz", hash = "sha256:86c9351f94f95de9db8a04ad8e892da299a088a64fd283f9f6f18770ae5eae1b"}, {file = "traitlets-5.0.4.tar.gz", hash = "sha256:86c9351f94f95de9db8a04ad8e892da299a088a64fd283f9f6f18770ae5eae1b"},

View File

@ -18,6 +18,26 @@ exclude = '''
) )
''' '''
[tool.djlint]
indent = 2
profile = "jinja"
blank_line_after_tag = "if,for,include,load,extends,block"
# H006: Images should have a height attribute
# H013: Images should have an alt attribute
# H016: Missing title tag in html. | False positive on template
# H017: Tag should be self closing
# H019: Replace 'javascript:abc()' with on_ event and real url. (javascript:history.back())
# H021: Avoid inline styles
# H025: Tag seems to be orphan (it messes up with indicators on strings such as <email@example.com>)
# H030: Consider adding a meta description | False positive on template
# H031: Consider adding meta keywords
# T003: Endblock should have a name
# J004: (Jinja) Static urls should follow {{ url_for('static'..) }} pattern
# J018: (Jinja) Internal links should use the {{ url_for() ... }} pattern. | Some false positives
# T001: Variables should be wrapped in a single whitespace. | Messes up with comments
ignore = "H006,H013,H016,H017,H019,H021,H025,H030,H031,T003,J004,J018,T001"
[tool.poetry] [tool.poetry]
name = "SimpleLogin" name = "SimpleLogin"
version = "0.1.0" version = "0.1.0"
@ -98,6 +118,7 @@ pre-commit = "^2.17.0"
black = "^22.1.0" black = "^22.1.0"
flake8 = "^4.0.1" flake8 = "^4.0.1"
flake8-bugbear = "^22.1.11" flake8-bugbear = "^22.1.11"
djlint = "0.7.3"
[build-system] [build-system]
requires = ["poetry>=0.12"] requires = ["poetry>=0.12"]

View File

@ -3,29 +3,21 @@
<label class="col-sm-2 col-form-label">{{ field.label }}</label> <label class="col-sm-2 col-form-label">{{ field.label }}</label>
<div class="col-sm-10"> <div class="col-sm-10">
{{ field(**kwargs)|safe }} {{ field(**kwargs)|safe }}
<small class="form-text text-muted">{{ field.description }}</small>
<small class="form-text text-muted">
{{ field.description }}
</small>
{% if field.errors %} {% if field.errors %}
<ul class=errors>
{% for error in field.errors %} <ul class="errors">
<li>{{ error }}</li> {% for error in field.errors %}<li>{{ error }}</li>{% endfor %}
{% endfor %}
</ul> </ul>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endmacro %} {% endmacro %}
{% macro render_field_errors(field) %} {% macro render_field_errors(field) %}
{% if field.errors %} {% if field.errors %}
<ul class=errors>
{% for error in field.errors %} <ul class="errors">
<li class="text-danger">{{ error }}</li> {% for error in field.errors %}<li class="text-danger">{{ error }}</li>{% endfor %}
{% endfor %}
</ul> </ul>
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}

View File

@ -1,16 +1,13 @@
{% extends "error.html" %} {% extends "error.html" %}
{% block error_name %} {% block error_name %}{{ error }}{% endblock %}
{{ error }}
{% endblock %}
{% block error_description %} {% block error_description %}
{% if show_resend_activation %} {% if show_resend_activation %}
<div class="text-center text-muted small mt-4"> <div class="text-center text-muted small mt-4">
Ask for another activation email? Ask for another activation email?
<a href="{{ url_for('auth.resend_activation') }}" style="color: #4d21ff">Resend</a> <a href="{{ url_for('auth.resend_activation') }}" style="color: #4d21ff">Resend</a>
</div> </div>
{% endif %} {% endif %}
{% endblock %}
{% endblock %}

View File

@ -1,21 +1,21 @@
{% extends "single.html" %} {% extends "single.html" %}
{% block title %} {% block title %}Change Email{% endblock %}
Change Email
{% endblock %}
{% block single_content %} {% block single_content %}
<div class="card"> <div class="card">
<div class="card-body p-6"> <div class="card-body p-6">
<div class="h3 text-center">Email Update</div> <div class="h3 text-center">Email Update</div>
<div class="text-danger text-center h4"> <div class="text-danger text-center h4">
Incorrect or expired link. <br><br> Incorrect or expired link.
<br />
<br />
</div> </div>
<div class="text-center"> <div class="text-center">
Please go to <a href="{{ url_for('dashboard.setting') }}">settings</a> Please go to
<a href="{{ url_for('dashboard.setting') }}">settings</a>
page to re-send the confirmation email. page to re-send the confirmation email.
</div> </div>
</div> </div>
</div> </div>
{% endblock %}
{% endblock %}

View File

@ -1,56 +1,47 @@
{% extends "single.html" %} {% extends "single.html" %}
{% block title %}Verify Your Security Key{% endblock %}
{% block title %}
Verify Your Security Key
{% endblock %}
{% block head %} {% block head %}
<script src="{{ url_for('static', filename='assets/js/vendors/base64.js') }}"></script> <script src="{{ url_for('static', filename='assets/js/vendors/base64.js') }}"></script>
<script src="{{ url_for('static', filename='assets/js/vendors/webauthn.js') }}"></script> <script src="{{ url_for('static', filename='assets/js/vendors/webauthn.js') }}"></script>
{% endblock %} {% endblock %}
{% block single_content %} {% block single_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="mb-2"> <div class="mb-2">
Your account is protected with your security key (WebAuthn). <br><br> Your account is protected with your security key (WebAuthn).
<br />
<br />
Follow your browser's steps to continue the sign-in process. Follow your browser's steps to continue the sign-in process.
</div> </div>
<form id="formRegisterKey" method="post"> <form id="formRegisterKey" method="post">
{{ fido_token_form.csrf_token }} {{ fido_token_form.csrf_token }}
{{ fido_token_form.sk_assertion(class="form-control", placeholder="") }} {{ fido_token_form.sk_assertion(class="form-control", placeholder="") }}
<div class="text-center"> <div class="text-center">
<button id="btnVerifyKey" class="btn btn-success mt-2" onclick="verifyKey();">Use your security key</button> <button id="btnVerifyKey" class="btn btn-success mt-2" onclick="verifyKey();">Use your security key</button>
</div> </div>
<div class="form-check"> <div class="form-check">
{{ fido_token_form.remember(class="form-check-input", id="remember") }} {{ fido_token_form.remember(class="form-check-input", id="remember") }}
<label class="form-check-label" for="remember"> <label class="form-check-label" for="remember">{{ fido_token_form.remember.description }}</label>
{{ fido_token_form.remember.description }}
</label>
</div> </div>
</form> </form>
{% if enable_otp %} {% if enable_otp %}
<hr>
<hr />
<div class="text-muted mt-5" style="margin-top: 1em;"> <div class="text-muted mt-5" style="margin-top: 1em;">
Don't have your key with you? <br> <a href="{{ url_for('auth.mfa') }}">Verify by One-Time Password</a> Don't have your key with you?
<br />
<a href="{{ url_for('auth.mfa') }}">Verify by One-Time Password</a>
</div> </div>
{% endif %} {% endif %}
<hr />
<hr>
<div class="mt-5"> <div class="mt-5">
If you have troubles with your authentication app, you can use the recovery code to login. <br> If you have troubles with your authentication app, you can use the recovery code to login.
<br />
<a href="{{ url_for('auth.recovery_route', next=next_url) }}">Use Recovery Codes</a> <a href="{{ url_for('auth.recovery_route', next=next_url) }}">Use Recovery Codes</a>
</div> </div>
<script> <script>
async function verifyKey() { async function verifyKey() {
$("#btnVerifyKey").prop('disabled', true); $("#btnVerifyKey").prop('disabled', true);
@ -78,12 +69,7 @@
} }
</script> </script>
{% if auto_activate %}<script>$('document').ready(verifyKey());</script>{% endif %}
{% if auto_activate %}
<script>$('document').ready(verifyKey());</script>
{% endif %}
</div> </div>
</div> </div>
{% endblock %}
{% endblock %}

View File

@ -1,34 +1,26 @@
{% extends "single.html" %} {% extends "single.html" %}
{% block title %} {% block title %}Forgot Password{% endblock %}
Forgot Password
{% endblock %}
{% block single_content %} {% block single_content %}
{% if error %}
<div class="text-danger text-center mb-4">{{ error }}</div>
{% endif %}
{% if error %}<div class="text-danger text-center mb-4">{{ error }}</div>{% endif %}
<form class="card" method="post"> <form class="card" method="post">
{{ form.csrf_token }} {{ form.csrf_token }}
<div class="card-body p-6"> <div class="card-body p-6">
<h1 class="card-title">Forgot password</h1> <h1 class="card-title">Forgot password</h1>
<div class="form-group"> <div class="form-group">
<label class="form-label">Email address</label> <label class="form-label">Email address</label>
{{ form.email(class="form-control", type="email") }} {{ form.email(class="form-control", type="email") }}
{{ render_field_errors(form.email) }} {{ render_field_errors(form.email) }}
</div> </div>
<div class="form-footer"> <div class="form-footer">
<button type="submit" class="btn btn-primary btn-block">Reset Password</button> <button type="submit" class="btn btn-primary btn-block">Reset Password</button>
</div> </div>
</div> </div>
</form> </form>
<div class="text-center text-muted"> <div class="text-center text-muted">
Forget it, <a href="{{ url_for('auth.login') }}">send me back</a> to the sign in screen. Forget it,
<a href="{{ url_for('auth.login') }}">send me back</a>
to the sign in screen.
</div> </div>
{% endblock %}
{% endblock %}

View File

@ -1,62 +1,52 @@
{% extends "single.html" %} {% extends "single.html" %}
{% block title %} {% block title %}Login{% endblock %}
Login
{% endblock %}
{% block single_content %} {% block single_content %}
{% if show_resend_activation %} {% if show_resend_activation %}
<div class="text-center text-muted small mb-4"> <div class="text-center text-muted small mb-4">
You haven't received the activation email? You haven't received the activation email?
<a href="{{ url_for('auth.resend_activation') }}">Resend</a> <a href="{{ url_for('auth.resend_activation') }}">Resend</a>
</div> </div>
{% endif %} {% endif %}
<div class="card" style="border-radius: 2%"> <div class="card" style="border-radius: 2%">
<div class="card-body p-6"> <div class="card-body p-6">
<h1 class="card-title">Welcome back!</h1> <h1 class="card-title">Welcome back!</h1>
<form method="post"> <form method="post">
{{ form.csrf_token }} {{ form.csrf_token }}
<div class="form-group"> <div class="form-group">
<label class="form-label">Email address</label> <label class="form-label">Email address</label>
{{ form.email(class="form-control", type="email", autofocus="true") }} {{ form.email(class="form-control", type="email", autofocus="true") }}
{{ render_field_errors(form.email) }} {{ render_field_errors(form.email) }}
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label"> <label class="form-label">Password</label>
Password
</label>
{{ form.password(class="form-control", type="password") }} {{ form.password(class="form-control", type="password") }}
{{ render_field_errors(form.password) }} {{ render_field_errors(form.password) }}
<div class="text-muted"> <div class="text-muted">
<a href="{{ url_for('auth.forgot_password') }}" class="small"> <a href="{{ url_for('auth.forgot_password') }}" class="small">I forgot my password</a>
I forgot my password
</a>
</div> </div>
</div> </div>
<div class="form-footer"> <div class="form-footer">
<button type="submit" class="btn btn-primary btn-block">Log in</button> <button type="submit" class="btn btn-primary btn-block">Log in</button>
</div> </div>
</form> </form>
{% if connect_with_proton %} {% if connect_with_proton %}
<div class="text-center my-2 text-gray"><span>or</span></div>
<div class="text-center my-2 text-gray">
<span>or</span>
</div>
<a class="btn btn-primary btn-block mt-2 proton-button" <a class="btn btn-primary btn-block mt-2 proton-button"
href="{{ url_for("auth.proton_login", next=next_url) }}"> href="{{ url_for("auth.proton_login", next=next_url) }}">
<img class="mr-2" src="/static/images/proton.svg"> <img class="mr-2" src="/static/images/proton.svg" />
Login with Proton Login with Proton
</a> </a>
{% endif %} {% endif %}
</div> </div>
</div> </div>
<div class="text-center text-muted mt-2"> <div class="text-center text-muted mt-2">
Don't have an account yet? <a href="{{ url_for('auth.register') }}">Sign up</a> Don't have an account yet?
<a href="{{ url_for('auth.register') }}">Sign up</a>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,54 +1,47 @@
{% extends "single.html" %} {% extends "single.html" %}
{% block title %}MFA{% endblock %}
{% block title %}
MFA
{% endblock %}
{% block single_content %} {% block single_content %}
<div class="card"> <div class="card">
<div class="card-body p-6"> <div class="card-body p-6">
<div class="mb-2"> <div class="mb-2">
Your account is protected with Two Factor Authentication. <br><br> Your account is protected with Two Factor Authentication.
<br />
<br />
You will need to enter your 2FA authentication code. You will need to enter your 2FA authentication code.
</div> </div>
<form method="post"> <form method="post">
{{ otp_token_form.csrf_token }} {{ otp_token_form.csrf_token }}
<input type="hidden" name="form-name" value="create"> <input type="hidden" name="form-name" value="create" />
<div class="font-weight-bold mt-5">Token</div> <div class="font-weight-bold mt-5">Token</div>
<div class="small-text mb-3">Please enter the 2FA code from your 2FA authenticator</div> <div class="small-text mb-3">Please enter the 2FA code from your 2FA authenticator</div>
{{ otp_token_form.token(class="form-control", autofocus="true") }} {{ otp_token_form.token(class="form-control", autofocus="true") }}
{{ render_field_errors(otp_token_form.token) }} {{ render_field_errors(otp_token_form.token) }}
<div class="form-check"> <div class="form-check">
{{ otp_token_form.remember(class="form-check-input", id="remember") }} {{ otp_token_form.remember(class="form-check-input", id="remember") }}
<label class="form-check-label" for="remember"> <label class="form-check-label" for="remember">{{ otp_token_form.remember.description }}</label>
{{ otp_token_form.remember.description }}
</label>
</div> </div>
<button class="btn btn-success mt-2">Submit</button> <button class="btn btn-success mt-2">Submit</button>
</form> </form>
{% if enable_fido %} {% if enable_fido %}
<hr>
<hr />
<div class="text-muted mt-5" style="margin-top: 1em;"> <div class="text-muted mt-5" style="margin-top: 1em;">
Having trouble with your authenticator? <br> <a href="{{ url_for('auth.fido') }}">Verify by your security Having trouble with your authenticator?
key</a> <br />
<a href="{{ url_for('auth.fido') }}">
Verify by your security
key
</a>
</div> </div>
{% endif %} {% endif %}
<hr />
<hr>
<div class="mt-5"> <div class="mt-5">
If you cannot access your authenticator application you can instead use a recovery code. <br> If you cannot access your authenticator application you can instead use a recovery code.
<br />
<a href="{{ url_for('auth.recovery_route', next=next_url) }}">Use Recovery Code</a> <a href="{{ url_for('auth.recovery_route', next=next_url) }}">Use Recovery Code</a>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,27 +1,18 @@
{% extends "single.html" %} {% extends "single.html" %}
{% block title %} {% block title %}Recovery Code{% endblock %}
Recovery Code
{% endblock %}
{% block single_content %} {% block single_content %}
<div class="card"> <div class="card">
<div class="card-body p-6"> <div class="card-body p-6">
<form method="post"> <form method="post">
{{ recovery_form.csrf_token }} {{ recovery_form.csrf_token }}
<div class="font-weight-bold mt-5">Code</div> <div class="font-weight-bold mt-5">Code</div>
<div class="small-text">Please enter one of the recovery codes here <div class="small-text">Please enter one of the recovery codes here</div>
</div>
{{ recovery_form.code(class="form-control", autofocus="true") }} {{ recovery_form.code(class="form-control", autofocus="true") }}
{{ render_field_errors(recovery_form.code) }} {{ render_field_errors(recovery_form.code) }}
<button class="btn btn-success mt-2">Submit</button> <button class="btn btn-success mt-2">Submit</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %}
{% endblock %}

View File

@ -1,15 +1,12 @@
{% extends "single.html" %} {% extends "single.html" %}
{% block title %} {% block title %}Register{% endblock %}
Register
{% endblock %}
{% block single_content %} {% block single_content %}
<form class="card" style="border-radius: 2%" method="post"> <form class="card" style="border-radius: 2%" method="post">
{{ form.csrf_token }} {{ form.csrf_token }}
<div class="card-body p-6"> <div class="card-body p-6">
<h1 class="card-title">Create new account</h1> <h1 class="card-title">Create new account</h1>
<div class="form-group"> <div class="form-group">
<label class="form-label">Email address</label> <label class="form-label">Email address</label>
{{ form.email(class="form-control", type="email") }} {{ form.email(class="form-control", type="email") }}
@ -19,13 +16,11 @@
</div> </div>
{{ render_field_errors(form.email) }} {{ render_field_errors(form.email) }}
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Password</label> <label class="form-label">Password</label>
{{ form.password(class="form-control", type="password") }} {{ form.password(class="form-control", type="password") }}
{{ render_field_errors(form.password) }} {{ render_field_errors(form.password) }}
</div> </div>
<!-- TODO: add terms <!-- TODO: add terms
<div class="form-group"> <div class="form-group">
<label class="custom-control custom-checkbox"> <label class="custom-control custom-checkbox">
@ -33,34 +28,34 @@
<span class="custom-control-label">Agree the <a href="terms.html">terms and policy</a></span> <span class="custom-control-label">Agree the <a href="terms.html">terms and policy</a></span>
</label> </label>
</div> </div>
--> -->
{% if HCAPTCHA_SITEKEY %} {% if HCAPTCHA_SITEKEY %}
<div class="h-captcha" data-sitekey="{{ HCAPTCHA_SITEKEY }}"></div> <div class="h-captcha" data-sitekey="{{ HCAPTCHA_SITEKEY }}"></div>
<script src="https://hcaptcha.com/1/api.js" async defer></script> <script src="https://hcaptcha.com/1/api.js" async defer></script>
{% endif %} {% endif %}
<small class="text-center mt-3"> <small class="text-center mt-3">
By clicking Create Account, you agree to abide by By clicking Create Account, you agree to abide by
<a href="https://simplelogin.io/terms">SimpleLogin's Terms and Conditions.</a> <a href="https://simplelogin.io/terms">SimpleLogin's Terms and Conditions.</a>
</small> </small>
<div class="mt-2"> <div class="mt-2">
<button type="submit" class="btn btn-primary btn-block">Create Account</button> <button type="submit" class="btn btn-primary btn-block">Create Account</button>
</div> </div>
{% if connect_with_proton %} {% if connect_with_proton %}
<div class="text-center my-2 text-gray"><span>or</span></div>
<div class="text-center my-2 text-gray">
<span>or</span>
</div>
<a class="btn btn-primary btn-block mt-2 proton-button" <a class="btn btn-primary btn-block mt-2 proton-button"
href="{{ url_for("auth.proton_login", next=next_url) }}"> href="{{ url_for("auth.proton_login", next=next_url) }}">
<img class="mr-2" src="/static/images/proton.svg"> <img class="mr-2" src="/static/images/proton.svg" />
Sign up with Proton Sign up with Proton
</a> </a>
{% endif %} {% endif %}
</div> </div>
</form> </form>
<div class="text-center text-muted mb-6"> <div class="text-center text-muted mb-6">
Already have account? <a href="{{ url_for('auth.login') }}">Sign in</a> Already have account?
<a href="{{ url_for('auth.login') }}">Sign in</a>
</div> </div>
{% endblock %}
{% endblock %}

View File

@ -1,22 +1,14 @@
{% extends "single.html" %} {% extends "single.html" %}
{% block title %} {% block title %}Activation Email Sent{% endblock %}
Activation Email Sent
{% endblock %}
{% block single_content %} {% block single_content %}
<div class="card"> <div class="card">
<div class="card-body p-6 text-center"> <div class="card-body p-6 text-center">
<h1 class="h4">An email to validate your email is on its way.</h1>
<h1 class="h4">
An email to validate your email is on its way.
</h1>
<p> <p>
Please check your inbox/spam folder. Please check your inbox/spam folder.
</p> </p>
</div> </div>
</div> </div>
{% endblock %}
{% endblock %}

View File

@ -1,29 +1,24 @@
{% extends "single.html" %} {% extends "single.html" %}
{% block title %} {% block title %}Resend activation email{% endblock %}
Resend activation email
{% endblock %}
{% block single_content %} {% block single_content %}
<form class="card" method="post"> <form class="card" method="post">
{{ form.csrf_token }} {{ form.csrf_token }}
<div class="card-body p-6"> <div class="card-body p-6">
<div class="card-title">Resend activation email</div> <div class="card-title">Resend activation email</div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Email address</label> <label class="form-label">Email address</label>
{{ form.email(class="form-control", type="email") }} {{ form.email(class="form-control", type="email") }}
{{ render_field_errors(form.email) }} {{ render_field_errors(form.email) }}
</div> </div>
<div class="form-footer"> <div class="form-footer">
<button type="submit" class="btn btn-primary btn-block">Resend</button> <button type="submit" class="btn btn-primary btn-block">Resend</button>
</div> </div>
</div> </div>
</form> </form>
<div class="text-center text-muted"> <div class="text-center text-muted">
Don't have account yet? <a href="{{ url_for('auth.register') }}">Sign up</a> Don't have account yet?
<a href="{{ url_for('auth.register') }}">Sign up</a>
</div> </div>
{% endblock %}
{% endblock %}

View File

@ -1,29 +1,21 @@
{% extends "single.html" %} {% extends "single.html" %}
{% block title %} {% block title %}Reset password{% endblock %}
Reset password
{% endblock %}
{% block single_content %} {% block single_content %}
{% if error %}
<div class="text-danger text-center mb-4">{{ error }}</div>
{% endif %}
{% if error %}<div class="text-danger text-center mb-4">{{ error }}</div>{% endif %}
<form class="card" method="post"> <form class="card" method="post">
{{ form.csrf_token }} {{ form.csrf_token }}
<div class="card-body p-6"> <div class="card-body p-6">
<div class="card-title">Reset your password</div> <div class="card-title">Reset your password</div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Password</label> <label class="form-label">Password</label>
{{ form.password(class="form-control", type="password") }} {{ form.password(class="form-control", type="password") }}
{{ render_field_errors(form.password) }} {{ render_field_errors(form.password) }}
</div> </div>
<div class="form-footer"> <div class="form-footer">
<button type="submit" class="btn btn-primary btn-block">Reset</button> <button type="submit" class="btn btn-primary btn-block">Reset</button>
</div> </div>
</div> </div>
</form> </form>
{% endblock %}
{% endblock %}

View File

@ -1,52 +1,46 @@
{% extends "single.html" %} {% extends "single.html" %}
{% block title %}Social Login{% endblock %}
{% block title %}
Social Login
{% endblock %}
{% block single_content %} {% block single_content %}
<div class="card"> <div class="card">
<div class="card-body p-6"> <div class="card-body p-6">
<div class="card-title text-center">Social login <div class="card-title text-center">Social login</div>
</div>
{% if GITHUB_CLIENT_ID %} {% if GITHUB_CLIENT_ID %}
<a href="{{ url_for('auth.github_login', next=next_url) }}"
class="btn btn-block btn-social btn-github">
<i class="fa fa-github"></i> Sign in with GitHub
</a>
{% endif %}
<a href="{{ url_for('auth.github_login', next=next_url) }}"
class="btn btn-block btn-social btn-github">
<i class="fa fa-github"></i> Sign in with GitHub
</a>
{% endif %}
{% if GOOGLE_CLIENT_ID %} {% if GOOGLE_CLIENT_ID %}
<a href="{{ url_for('auth.google_login', next=next_url) }}"
class="btn btn-block btn-social btn-google">
<i class="fa fa-google"></i> Sign in with Google
</a>
{% endif %}
<a href="{{ url_for('auth.google_login', next=next_url) }}"
class="btn btn-block btn-social btn-google">
<i class="fa fa-google"></i> Sign in with Google
</a>
{% endif %}
{% if FACEBOOK_CLIENT_ID %} {% if FACEBOOK_CLIENT_ID %}
<a href="{{ url_for('auth.facebook_login', next=next_url) }}"
class="btn btn-block btn-social btn-facebook"> <a href="{{ url_for('auth.facebook_login', next=next_url) }}"
<i class="fa fa-facebook"></i> Sign in with Facebook class="btn btn-block btn-social btn-facebook">
</a> <i class="fa fa-facebook"></i> Sign in with Facebook
</a>
{% endif %} {% endif %}
</div> </div>
<div class="text-center p-3" style="font-size: 12px; font-weight: 300; margin: auto"> <div class="text-center p-3"
style="font-size: 12px; font-weight: 300; margin: auto">
<span class="badge badge-warning">Warning</span> <span class="badge badge-warning">Warning</span>
Please note that social login is now <b>deprecated</b>. <br><br> Please note that social login is now <b>deprecated</b>.
<br />
<br />
Though practical, these social providers do not respect your privacy and therefore we recommend using Though practical, these social providers do not respect your privacy and therefore we recommend using
email/password. email/password.
</div> </div>
</div> </div>
<div class="text-center text-muted mt-2"> <div class="text-center text-muted mt-2">
<a href="{{ url_for('auth.register') }}">Sign up</a> / <a href="{{ url_for('auth.login') }}">Login</a> <a href="{{ url_for('auth.register') }}">Sign up</a>
/
<a href="{{ url_for('auth.login') }}">Login</a>
</div> </div>
{% endblock %}
{% endblock %}

View File

@ -1,132 +1,105 @@
{% from "_formhelpers.html" import render_field, render_field_errors %} {% from "_formhelpers.html" import render_field, render_field_errors %}
<!doctype html> <!doctype html>
<html lang="en" dir="ltr" data-theme="{% if request.cookies.get('dark-mode') == 'true' %}dark{% endif %}"> <html lang="en"
<head> dir="ltr"
<meta charset="UTF-8"> data-theme="{%- if request.cookies.get('dark-mode') == 'true' -%} dark{%- endif -%}">
<meta name="viewport" <head>
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta name="viewport"
<meta http-equiv="Content-Language" content="en"/> content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
<meta name="msapplication-TileColor" content="#2d89ef"> <meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta name="theme-color" content="#4188c9"> <meta http-equiv="Content-Language" content="en" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/> <meta name="msapplication-TileColor" content="#2d89ef" />
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="theme-color" content="#4188c9" />
<meta name="mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style"
<meta name="HandheldFriendly" content="True"> content="black-translucent"/>
<meta name="MobileOptimized" content="320"> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="referrer" content="no-referrer"> <meta name="mobile-web-app-capable" content="yes" />
<meta name="HandheldFriendly" content="True" />
<meta name="MobileOptimized" content="320" />
<meta name="referrer" content="no-referrer" />
<!-- Bing -->
<meta name="msvalidate.01" content="2A313A69CBFD1A378C3B91734DC221A8" />
<!-- Yandex -->
<meta name="yandex-verification" content="c9e5d4d68bc983a1" />
<meta name="description"
content="Protect your email address with email ALIAS. Create a different email alias for each website. No more phishing, spams."/>
<link rel="icon" href="/static/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico" />
<link rel="canonical" href="{{ CANONICAL_URL }}" />
<title>
{% block title %}{% endblock %}
| SimpleLogin
</title>
<link rel="stylesheet"
href="{{ url_for('static', filename='node_modules/font-awesome/css/font-awesome.css') }}"/>
<!-- Dashboard Core -->
<link href="/static/assets/css/dashboard.css" rel="stylesheet" />
<!-- Tabler JS -->
<script src="/static/assets/js/vendors/jquery-3.2.1.min.js"></script>
<script src="/static/assets/js/vendors/bootstrap.bundle.min.js"></script>
<script src="/static/assets/js/vendors/jquery.sparkline.min.js"></script>
<script src="/static/assets/js/vendors/selectize.min.js"></script>
<script src="/static/assets/js/vendors/jquery.tablesorter.min.js"></script>
<script src="/static/assets/js/vendors/jquery-jvectormap-2.0.3.min.js"></script>
<script src="/static/assets/js/vendors/jquery-jvectormap-de-merc.js"></script>
<script src="/static/assets/js/vendors/jquery-jvectormap-world-mill.js"></script>
<script src="/static/assets/js/vendors/circle-progress.min.js"></script>
<script src="/static/assets/js/core.js"></script>
<!-- ClipboardJS -->
<script src="/static/vendor/clipboard.min.js"></script>
<!-- IntroJS -->
<link rel="stylesheet"
type="text/css"
href="{{ url_for('static', filename='node_modules/intro.js/minified/introjs.min.css') }}"/>
<script src="{{ url_for('static', filename='node_modules/intro.js/minified/intro.min.js') }}"></script>
<!-- Sentry -->
<script src="{{ url_for('static', filename='node_modules/@sentry/browser/build/bundle.min.js') }}"></script>
<link rel="stylesheet" href="/static/vendor/bootstrap-social.min.css" />
<!-- Toastr library -->
<link rel="stylesheet"
href="{{ url_for('static', filename='node_modules/toastr/build/toastr.min.css') }}"/>
<script src="{{ url_for('static', filename='node_modules/toastr/build/toastr.min.js') }}"></script>
<script src="{{ url_for('static', filename='node_modules/bootbox/dist/bootbox.min.js') }}"></script>
<!-- Multiple-select library -->
<link rel="stylesheet"
href="{{ url_for('static', filename='node_modules/multiple-select/dist/multiple-select.min.css') }}"/>
<script src="{{ url_for('static', filename='node_modules/multiple-select/dist/multiple-select.min.js') }}"></script>
<!-- Parseley library -->
<script src="{{ url_for('static', filename='node_modules/parsleyjs/dist/parsley.min.js') }}"></script>
<script src="{{ url_for('static', filename='node_modules/parsleyjs/dist/i18n/en.js') }}"></script>
<script src="{{ url_for('static', filename='node_modules/htmx.org/dist/htmx.min.js') }}"></script>
{% if PLAUSIBLE_HOST and PLAUSIBLE_DOMAIN %}
<!-- Bing --> <!-- Plausible Analytics library -->
<meta name="msvalidate.01" content="2A313A69CBFD1A378C3B91734DC221A8"/> <script async defer data-domain=”{{ PLAUSIBLE_DOMAIN }} src=”{{ PLAUSIBLE_HOST }}/js/plausible.outbound-links.js></script>
{% endif %}
<link rel="stylesheet"
href="{{ url_for('static', filename='darkmode.css') }}?v={{ VERSION }}"/>
<link rel="stylesheet"
type="text/css"
href="/static/style.css?v={{ VERSION }}"/>
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
<script>toastr.options.closeButton = true;</script>
<!-- For additional head -->
{% block head %}{% endblock %}
</head>
<body>
<div class="page">
{% block announcement %}{% endblock %}
<div class="container">
<!-- For flash messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
<!-- Categories: success (green), info (blue), warning (yellow), danger (red) -->
{% if messages %}
<!-- Yandex --> {% for category, message in messages %}<script>toastr.{{category }}("{{ message }}");</script>{% endfor %}
<meta name="yandex-verification" content="c9e5d4d68bc983a1"/> {% endif %}
{% endwith %}
<meta name="description" </div>
content="Protect your email address with email ALIAS. Create a different email alias for each website. No more phishing, spams."> {% block content %}{% endblock %}
</div>
<link rel="icon" href="/static/favicon.ico" type="image/x-icon"/> <script>
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico"/>
<link rel="canonical" href="{{ CANONICAL_URL }}">
<title>
{% block title %}{% endblock %} | SimpleLogin
</title>
<link rel="stylesheet" href="{{ url_for('static', filename='node_modules/font-awesome/css/font-awesome.css') }}">
<!-- Dashboard Core -->
<link href="/static/assets/css/dashboard.css" rel="stylesheet"/>
<!-- Tabler JS -->
<script src="/static/assets/js/vendors/jquery-3.2.1.min.js"></script>
<script src="/static/assets/js/vendors/bootstrap.bundle.min.js"></script>
<script src="/static/assets/js/vendors/jquery.sparkline.min.js"></script>
<script src="/static/assets/js/vendors/selectize.min.js"></script>
<script src="/static/assets/js/vendors/jquery.tablesorter.min.js"></script>
<script src="/static/assets/js/vendors/jquery-jvectormap-2.0.3.min.js"></script>
<script src="/static/assets/js/vendors/jquery-jvectormap-de-merc.js"></script>
<script src="/static/assets/js/vendors/jquery-jvectormap-world-mill.js"></script>
<script src="/static/assets/js/vendors/circle-progress.min.js"></script>
<script src="/static/assets/js/core.js"></script>
<!-- ClipboardJS -->
<script src="/static/vendor/clipboard.min.js"></script>
<!-- IntroJS -->
<link rel="stylesheet" type="text/css"
href="{{ url_for('static', filename='node_modules/intro.js/minified/introjs.min.css') }}">
<script src="{{ url_for('static', filename='node_modules/intro.js/minified/intro.min.js') }}"></script>
<!-- Sentry -->
<script src="{{ url_for('static', filename='node_modules/@sentry/browser/build/bundle.min.js') }}">
</script>
<link rel="stylesheet" href="/static/vendor/bootstrap-social.min.css">
<!-- Toastr library -->
<link rel="stylesheet" href="{{ url_for('static', filename='node_modules/toastr/build/toastr.min.css') }}">
<script src="{{ url_for('static', filename='node_modules/toastr/build/toastr.min.js') }}"></script>
<script src="{{ url_for('static', filename='node_modules/bootbox/dist/bootbox.min.js') }}"></script>
<!-- Multiple-select library -->
<link rel="stylesheet"
href="{{ url_for('static', filename='node_modules/multiple-select/dist/multiple-select.min.css') }}">
<script
src="{{ url_for('static', filename='node_modules/multiple-select/dist/multiple-select.min.js') }}"></script>
<!-- Parseley library -->
<script src="{{ url_for('static', filename='node_modules/parsleyjs/dist/parsley.min.js') }}"></script>
<script src="{{ url_for('static', filename='node_modules/parsleyjs/dist/i18n/en.js') }}"></script>
<script src="{{ url_for('static', filename='node_modules/htmx.org/dist/htmx.min.js') }}"></script>
{% if PLAUSIBLE_HOST and PLAUSIBLE_DOMAIN %}
<!-- Plausible Analytics library -->
<script async defer data-domain=”{{ PLAUSIBLE_DOMAIN }} src=”{{ PLAUSIBLE_HOST }}/js/plausible.outbound-links.js></script>
{% endif %}
<link rel="stylesheet" href="{{ url_for('static', filename='darkmode.css') }}?v={{ VERSION }}">
<link rel="stylesheet" type="text/css" href="/static/style.css?v={{ VERSION }}">
<script src="{{ url_for('static', filename='js/theme.js') }}"></script>
<script>
toastr.options.closeButton = true;
</script>
<!-- For additional head -->
{% block head %}
{% endblock %}
</head>
<body class="">
<div class="page">
{% block announcement %}
{% endblock %}
<div class="container">
<!-- For flash messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
<!-- Categories: success (green), info (blue), warning (yellow), danger (red) -->
{% if messages %}
{% for category, message in messages %}
<script>
toastr.{{category}}("{{ message }}");
</script>
{% endfor %}
{% endif %}
{% endwith %}
</div>
{% block content %}
{% endblock %}
</div>
<script>
{% if SENTRY_DSN %} {% if SENTRY_DSN %}
console.log("Init sentry"); console.log("Init sentry");
try { try {
@ -206,14 +179,10 @@
toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error"); toastr.error("Sorry for the inconvenience! Could you refresh the page & retry please?", "Unknown Error");
}); });
</script> </script>
<script src="{{ url_for('static', filename='local-storage-polyfill.js') }}"></script>
<script src="{{ url_for('static', filename='local-storage-polyfill.js') }}"></script> <!-- For additional script -->
{% block script %}{% endblock %}
<!-- For additional script --> <script src="{{ url_for('static', filename='js/analytics.js') }}"></script>
{% block script %} </body>
{% endblock %} </html>
<script src="{{ url_for('static', filename='js/analytics.js') }}"></script>
</body>
</html>

View File

@ -1,42 +1,49 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}Alias Contact Manager{% endblock %}
{% block title %}
Alias Contact Manager
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h1 class="h3"> {{ alias.email }} contacts <h1 class="h3">
<a class="ml-3 text-info" style="font-size: 12px" data-toggle="collapse" href="#howtouse" role="button" {{ alias.email }} contacts
aria-expanded="false" aria-controls="collapseExample"> <a class="ml-3 text-info"
style="font-size: 12px"
data-toggle="collapse"
href="#howtouse"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
How to use <i class="fe fe-chevrons-down"></i> How to use <i class="fe fe-chevrons-down"></i>
</a> </a>
</h1> </h1>
<div class="alert alert-primary collapse" id="howtouse" role="alert"> <div class="alert alert-primary collapse" id="howtouse" role="alert">
<p> <p>
To send an email from your alias to a contact, you need to create a <em>reverse-alias</em>, To send an email from your alias to a contact, you need to create a <em>reverse-alias</em>,
a special email address. <br> a special email address.
<br />
When you send an email to the reverse-alias, the email will be sent <b>from your alias</b> to the contact. When you send an email to the reverse-alias, the email will be sent <b>from your alias</b> to the contact.
<br> <br />
<img src="/static/images/reverse-alias.svg"
<img src="/static/images/reverse-alias.svg" style="border: 1px solid" class="my-2 img-fluid"> style="border: 1px solid"
class="my-2 img-fluid"/>
</p> </p>
<p> <p>
This might seem like "magic" but trust us, only the first time is a bit awkward. This might seem like "magic" but trust us, only the first time is a bit awkward.
</p> </p>
<p> <p>
{% if alias.mailbox_id %} {% if alias.mailbox_id %}
{% if alias.mailboxes | length == 1 %} {% if alias.mailboxes | length == 1 %}
Make sure you send the email from your mailbox <b>{{ alias.mailbox.email }}</b>. Make sure you send the email from your mailbox <b>{{ alias.mailbox.email }}</b>.
{% else %} {% else %}
Make sure you send the email from one of the following mailboxes: <br> Make sure you send the email from one of the following mailboxes:
<br />
{% for mailbox in alias.mailboxes %} {% for mailbox in alias.mailboxes %}
- <b>{{ mailbox.email }}</b> <br>
- <b>{{ mailbox.email }}</b>
<br />
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% else %} {% else %}
@ -52,42 +59,44 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row mb-5"> <div class="row mb-5">
<div class="col-12 col-lg-6 pt-1"> <div class="col-12 col-lg-6 pt-1">
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="create"> <input type="hidden" name="form-name" value="create" />
{{ new_contact_form.csrf_token }} {{ new_contact_form.csrf_token }}
{{ new_contact_form.email(class="form-control", placeholder="First Last <email@example.com>", autofocus=True) }} {{ new_contact_form.email(class="form-control", placeholder="First Last <email@example.com>", autofocus=True) }}
{{ render_field_errors(new_contact_form.email) }} {{ render_field_errors(new_contact_form.email) }}
<div class="small-text"> <div class="small-text">Where do you want to send the email?</div>
Where do you want to send the email?
</div>
{% if can_create_contacts %} {% if can_create_contacts %}
<button class="btn btn-primary mt-2">Create reverse-alias</button> <button class="btn btn-primary mt-2">Create reverse-alias</button>
{% else %} {% else %}
<button disabled title='Upgrade to premium to create reverse-aliases' class="btn btn-primary mt-2">Create reverse-alias</button> <button disabled
title="Upgrade to premium to create reverse-aliases"
class="btn btn-primary mt-2">
Create reverse-alias
</button>
{% endif %} {% endif %}
</form> </form>
</div> </div>
<div class="col-12 col-lg-6 pt-1"> <div class="col-12 col-lg-6 pt-1">
<div class="float-right d-flex"> <div class="float-right d-flex">
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="search"> <input type="hidden" name="form-name" value="search">
<input type="search"
<input type="search" name="query" value="{{ query }}" name="query"
value="{{ query }}"
placeholder="Enter to search for contacts" placeholder="Enter to search for contacts"
class="form-control shadow mr-2" class="form-control shadow mr-2"
style="max-width: 20em" style="max-width: 20em">
>
</form> </form>
{% if query %} {% if query %}
{% if highlight_contact_id %} {% if highlight_contact_id %}
<a href="{{ url_for("dashboard.alias_contact_manager", alias_id=alias.id, highlight_contact_id=highlight_contact_id) }}" <a href="{{ url_for("dashboard.alias_contact_manager", alias_id=alias.id, highlight_contact_id=highlight_contact_id) }}"
class="btn btn-light">Reset</a> class="btn btn-light">
Reset
</a>
{% else %} {% else %}
<a href="{{ url_for("dashboard.alias_contact_manager", alias_id=alias.id) }}" <a href="{{ url_for("dashboard.alias_contact_manager", alias_id=alias.id) }}"
class="btn btn-light">Reset</a> class="btn btn-light">Reset</a>
@ -96,105 +105,97 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
{% for contact_info in contact_infos %} {% for contact_info in contact_infos %}
{% set contact = contact_info.contact %} {% set contact = contact_info.contact %}
<div class="col-md-6"> <div class="col-md-6">
<div class="my-2 p-2 card {% if contact.id == highlight_contact_id %} highlight-row {% endif %}"> <div class="my-2 p-2 card {% if contact.id == highlight_contact_id %} highlight-row{% endif %}">
<div class="mb-2 row"> <div class="mb-2 row">
<div class="col"> <div class="col">
<span class="font-weight-bold">{{ contact.website_email }}</span> <span class="font-weight-bold">{{ contact.website_email }}</span>
{% if contact.pgp_finger_print %} {% if contact.pgp_finger_print %}
<span class="cursor" data-toggle="tooltip" data-original-title="PGP Enabled">🗝</span>
<span class="cursor"
data-toggle="tooltip"
data-original-title="PGP Enabled">🗝</span>
{% endif %} {% endif %}
</div> </div>
<div class="col text-right"> <div class="col text-right">
<label class="custom-switch cursor" <label class="custom-switch cursor" data-toggle="tooltip" {% if contact.block_forward %}
data-toggle="tooltip" title="Unblock sender - start receiving emails from this sender" {% else %} title="Block sender - stop receiving emails from this sender" {% endif %} style="padding-left: 0px">
{% if contact.block_forward %} <input type="checkbox" class="enable-disable-contact custom-switch-input" data-contact="{{ contact.id }}" data-contact-email="{{ contact.website_email }}" {{ "checked" if not contact.block_forward else "" }}>
title="Unblock sender - start receiving emails from this sender"
{% else %}
title="Block sender - stop receiving emails from this sender"
{% endif %}
style="padding-left: 0px"
>
<input type="checkbox" class="enable-disable-contact custom-switch-input"
data-contact="{{ contact.id }}"
data-contact-email="{{ contact.website_email }}"
{{ "checked" if not contact.block_forward else "" }}>
<span class="custom-switch-indicator"></span> <span class="custom-switch-indicator"></span>
</label> </label>
</div> </div>
</div> </div>
<div> <div>
<span> <span>
<a href="{{ 'mailto:' + contact.website_send_to() }}" <a href="{{ 'mailto:' + contact.website_send_to() }}"
data-toggle="tooltip" data-toggle="tooltip"
title="You can click on this to open your email client. Or use the copy button 👉" title="You can click on this to open your email client. Or use the copy button 👉"
class="font-weight-bold">*************************</a> class="font-weight-bold">
*************************
<span class="clipboard btn btn-sm btn-success copy-btn" data-toggle="tooltip" </a>
<span class="clipboard btn btn-sm btn-success copy-btn"
data-toggle="tooltip"
title="Copy the reverse-alias to clipboard" title="Copy the reverse-alias to clipboard"
data-clipboard-text="{{ contact.website_send_to() }}"> data-clipboard-text="{{ contact.website_send_to() }}">
Copy reverse-alias Copy reverse-alias
</span> </span>
</span> </span>
</div> </div>
<div class="mb-2 text-muted small-text"> <div class="mb-2 text-muted small-text">
{% if contact_info.latest_email_log != None %} {% if contact_info.latest_email_log != None %}
{% set email_log = contact_info.latest_email_log %}
{% set email_log = contact_info.latest_email_log %}
{% if email_log.is_reply %} {% if email_log.is_reply %}
<i class="fa fa-reply mr-2" data-toggle="tooltip" title="Email reply/sent from alias"></i>
<i class="fa fa-reply mr-2"
data-toggle="tooltip"
title="Email reply/sent from alias"></i>
{{ email_log.created_at | dt }} {{ email_log.created_at | dt }}
{% elif email_log.bounced %} {% elif email_log.bounced %}
<span class="text-danger"> <span class="text-danger">
<i class="fa fa-warning mr-2" data-toggle="tooltip" <i class="fa fa-warning mr-2"
data-toggle="tooltip"
title="Email bounced and cannot be forwarded to your mailbox"></i> title="Email bounced and cannot be forwarded to your mailbox"></i>
{{ email_log.created_at | dt }} {{ email_log.created_at | dt }}
</span> </span>
{% elif email_log.blocked %} {% elif email_log.blocked %}
<i class="fa fa-ban mr-2 text-danger" data-toggle="tooltip" title="Email blocked"></i> <i class="fa fa-ban mr-2 text-danger"
data-toggle="tooltip"
title="Email blocked"></i>
{{ email_log.created_at | dt }} {{ email_log.created_at | dt }}
{% else %} {% else %}
<i class="fa fa-paper-plane mr-2" data-toggle="tooltip" title="Email sent to alias"></i> <i class="fa fa-paper-plane mr-2"
data-toggle="tooltip"
title="Email sent to alias"></i>
{{ email_log.created_at | dt }} {{ email_log.created_at | dt }}
{% endif %} {% endif %}
<br> <br />
Contact created {{ contact.created_at | dt }} Contact created {{ contact.created_at | dt }}
{% else %} {% else %}
No Activity in the last 14 days. Contact created {{ contact.created_at | dt }} No Activity in the last 14 days. Contact created {{ contact.created_at | dt }}
{% endif %} {% endif %}
<div> <div>
<span class="alias-activity">{{ contact_info.nb_forward }}</span> forwards, <span class="alias-activity">{{ contact_info.nb_forward }}</span> forwards,
<span class="alias-activity">{{ contact_info.nb_reply }}</span> sents <span class="alias-activity">{{ contact_info.nb_reply }}</span> sents
in the last 14 days. in the last 14 days.
</div> </div>
</div> </div>
<a href="{{ url_for('dashboard.contact_detail_route', contact_id=contact.id) }}">Edit ➡</a> <a href="{{ url_for('dashboard.contact_detail_route', contact_id=contact.id) }}">Edit ➡</a>
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="delete"> <input type="hidden" name="form-name" value="delete">
<input type="hidden" name="contact-id" value="{{ contact.id }}"> <input type="hidden" name="contact-id" value="{{ contact.id }}">
<span class="card-link btn btn-link float-right delete-forward-email text-danger"> <span class="card-link btn btn-link float-right delete-forward-email text-danger">Delete</span>
Delete
</span>
</form> </form>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% if nb_contact > PAGE_LIMIT or page > 0 %} {% if nb_contact > PAGE_LIMIT or page > 0 %}
<div class="row mt-3"> <div class="row mt-3">
<div class="col"> <div class="col">
<nav aria-label="Contact navigation"> <nav aria-label="Contact navigation">
@ -202,23 +203,23 @@
<li class="page-item"> <li class="page-item">
<a class="btn btn-outline-secondary {% if page == 0 %}disabled{% endif %}" <a class="btn btn-outline-secondary {% if page == 0 %}disabled{% endif %}"
href="{{ url_for('dashboard.alias_contact_manager', alias_id=alias.id, page=page-1) }}"> href="{{ url_for('dashboard.alias_contact_manager', alias_id=alias.id, page=page-1) }}">
Previous</a> Previous
</a>
</li> </li>
<li class="page-item"> <li class="page-item">
<a class="btn btn-outline-secondary {% if last_page %}disabled{% endif %}" <a class="btn btn-outline-secondary {% if last_page %}disabled{% endif %}"
href="{{ url_for('dashboard.alias_contact_manager', alias_id=alias.id, page=page+1) }}"> href="{{ url_for('dashboard.alias_contact_manager', alias_id=alias.id, page=page+1) }}">
Next</a> Next
</a>
</li> </li>
</ul> </ul>
</nav> </nav>
</div> </div>
</div> </div>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
$(".delete-forward-email").on("click", function (e) { $(".delete-forward-email").on("click", function (e) {
let that = $(this); let that = $(this);
@ -282,4 +283,4 @@
} }
} }
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,7 +1,8 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block head %} {% block head %}
<style> <style>
{# https://bootsnipp.com/snippets/rljEW#} {# https://bootsnipp.com/snippets/rljEW#}
.card-counter { .card-counter {
@ -61,108 +62,102 @@
display: block; display: block;
font-size: 18px; font-size: 18px;
} }
</style> </style>
{% endblock %} {% endblock %}
{% block title %} {% block title %}Alias Activity{% endblock %}
Alias Activity
{% endblock %}
{% block default_content %} {% block default_content %}
<h1 class="h3">
{{ alias.email }}
</h1>
<div class="row">
<div class="col-md-3 col-sm-6">
<div class="card-counter primary">
<i class="fa fa-at"></i>
<span class="count-numbers">{{ total }}</span>
<span class="count-name">Email Handled</span>
</div>
</div>
<div class="col-md-3 col-sm-6">
<div class="card-counter primary">
<i class="fa fa-paper-plane"></i>
<span class="count-numbers">{{ email_forwarded }}</span>
<span class="count-name">Email Forwarded</span>
</div>
</div>
<div class="col-md-3 col-sm-6">
<div class="card-counter primary">
<i class="fa fa-reply"></i>
<span class="count-numbers">{{ email_replied }}</span>
<span class="count-name">Email Replied</span>
</div>
</div>
<div class="col-md-3 col-sm-6">
<div class="card-counter danger">
<i class="fa fa-ban"></i>
<span class="count-numbers">{{ email_blocked }}</span>
<span class="count-name">Email Blocked</span>
</div>
</div>
</div>
<div class="row mt-4">
{% for log in logs %}
<div class="col-lg-6">
<div class="my-2 p-2 card border-light">
<div class="font-weight-bold">{{ log.when | dt }} <h1 class="h3">{{ alias.email }}</h1>
<div class="float-right pr-3"> <div class="row">
{% if log.bounced %} <div class="col-md-3 col-sm-6">
⚠️ <div class="card-counter primary">
{% else %} <i class="fa fa-at"></i>
{% if log.is_reply %} <span class="count-numbers">{{ total }}</span>
<i class="fa fa-reply"></i> <span class="count-name">Email Handled</span>
{% elif log.blocked %} </div>
<i class="fa fa-ban text-danger"></i> </div>
{% else %} <div class="col-md-3 col-sm-6">
<i class="fa fa-paper-plane"></i> <div class="card-counter primary">
{% endif %} <i class="fa fa-paper-plane"></i>
{% endif %} <span class="count-numbers">{{ email_forwarded }}</span>
</div> <span class="count-name">Email Forwarded</span>
</div> </div>
</div>
<div class="col-md-3 col-sm-6">
<div class="card-counter primary">
<i class="fa fa-reply"></i>
<span class="count-numbers">{{ email_replied }}</span>
<span class="count-name">Email Replied</span>
</div>
</div>
<div class="col-md-3 col-sm-6">
<div class="card-counter danger">
<i class="fa fa-ban"></i>
<span class="count-numbers">{{ email_blocked }}</span>
<span class="count-name">Email Blocked</span>
</div>
</div>
</div>
<div class="row mt-4">
{% for log in logs %}
{% if log.bounced and not log.is_reply %} <div class="col-lg-6">
<div> <div class="my-2 p-2 card border-light">
<span class="mr-2">{{ log.website_email }}</span> <div class="font-weight-bold">
<img src="{{ url_for('static', filename='arrows/forward-arrow.svg') }}" class="arrow"> {{ log.when | dt }}
<span class="ml-2">{{ log.alias }}</span> <div class="float-right pr-3">
<img src="{{ url_for('static', filename='arrows/blocked-arrow.svg') }}" class="arrow"> {% if log.bounced %}
<span class="ml-2">{{ log.email_log.bounced_mailbox() }}</span>
</div>
{% elif log.bounced and log.is_reply %}
<div>
<span class="ml-2">{{ log.email_log.bounced_mailbox() }}</span>
<img src="{{ url_for('static', filename='arrows/forward-arrow.svg') }}" class="arrow">
<span class="ml-2">{{ log.alias }}</span>
<img src="{{ url_for('static', filename='arrows/blocked-arrow.svg') }}" class="arrow">
<span class="mr-2">{{ log.website_email }}</span>
</div>
{% else %}
<div>
{{ log.website_email }}
</div>
{% endif %}
⚠️
{% else %}
{% if log.is_reply %}
</div> <i class="fa fa-reply"></i>
</div> {% elif log.blocked %}
{% endfor %} <i class="fa fa-ban text-danger"></i>
</div> {% else %}
<i class="fa fa-paper-plane"></i>
{% endif %}
{% endif %}
</div>
</div>
{% if log.bounced and not log.is_reply %}
<nav aria-label="Alias log navigation"> <div>
<ul class="pagination"> <span class="mr-2">{{ log.website_email }}</span>
<li class="page-item"> <img src="{{ url_for('static', filename='arrows/forward-arrow.svg') }}" class="arrow">
<a class="btn btn-outline-secondary {% if page_id == 0 %}disabled{% endif %}" <span class="ml-2">{{ log.alias }}</span>
href="{{ url_for('dashboard.alias_log', alias_id=alias_id, page_id=page_id-1) }}">Previous</a> <img src="{{ url_for('static', filename='arrows/blocked-arrow.svg') }}" class="arrow">
</li> <span class="ml-2">{{ log.email_log.bounced_mailbox() }}</span>
<li class="page-item"> </div>
<a class="btn btn-outline-secondary {% if last_page %}disabled{% endif %}" {% elif log.bounced and log.is_reply %}
href="{{ url_for('dashboard.alias_log', alias_id=alias_id, page_id=page_id+1) }}">Next</a> <div>
</li> <span class="ml-2">{{ log.email_log.bounced_mailbox() }}</span>
</ul> <img src="{{ url_for('static', filename='arrows/forward-arrow.svg') }}" class="arrow">
</nav> <span class="ml-2">{{ log.alias }}</span>
{% endblock %} <img src="{{ url_for('static', filename='arrows/blocked-arrow.svg') }}" class="arrow">
<span class="mr-2">{{ log.website_email }}</span>
{% block script %} </div>
{% else %}
<div>{{ log.website_email }}</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<nav aria-label="Alias log navigation">
<ul class="pagination">
<li class="page-item">
<a class="btn btn-outline-secondary {% if page_id == 0 %}disabled{% endif %}" href="{{ url_for('dashboard.alias_log', alias_id=alias_id, page_id=page_id-1) }}">
Previous
</a>
</li>
<li class="page-item">
<a class="btn btn-outline-secondary {% if last_page %}disabled{% endif %}" href="{{ url_for('dashboard.alias_log', alias_id=alias_id, page_id=page_id+1) }}">
Next
</a>
</li>
</ul>
</nav>
{% endblock %} {% endblock %}
{% block script %}{% endblock %}

View File

@ -1,46 +1,31 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}Receive {{ alias.email }}{% endblock %}
{% block title %}
Receive {{ alias.email }}
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3">Receive {{ alias.email }}</h1> <h1 class="h3">Receive {{ alias.email }}</h1>
<p> <p>
You are invited to become the owner of the alias <b>{{ alias.email }}</b> You are invited to become the owner of the alias <b>{{ alias.email }}</b>
</p> </p>
<p> <p>
Please choose the mailbox(es) that owns this alias 👇 Please choose the mailbox(es) that owns this alias 👇
</p> </p>
<form method="post" class="mt-2"> <form method="post" class="mt-2">
<select data-width="100%" <select data-width="100%" class="mailbox-select" multiple name="mailbox_ids">
class="mailbox-select" multiple name="mailbox_ids">
{% for mailbox in mailboxes %} {% for mailbox in mailboxes %}
<option value="{{ mailbox.id }}" {% if mailbox.id == current_user.default_mailbox_id %}
selected {% endif %}> <option value="{{ mailbox.id }}"
{% if mailbox.id == current_user.default_mailbox_id %} selected{% endif %}>
{{ mailbox.email }} {{ mailbox.email }}
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
<button class="btn btn-success mt-2">Confirm</button> <button class="btn btn-success mt-2">Confirm</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %}<script>$('.mailbox-select').multipleSelect();</script>{% endblock %}
{% block script %}
<script>
$('.mailbox-select').multipleSelect();
</script>
{% endblock %}

View File

@ -1,47 +1,38 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}Send {{ alias.email }}{% endblock %}
{% block title %}
Send {{ alias.email }}
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3">Transfer {{ alias.email }}</h1> <h1 class="h3">Transfer {{ alias.email }}</h1>
<p> <p>
This page allows you to transfer {{ alias.email }} to another person so they can use it to receive and send This page allows you to transfer {{ alias.email }} to another person so they can use it to receive and send
emails. emails.
</p> </p>
{% if alias_transfer_url %} {% if alias_transfer_url %}
<em data-toggle="tooltip" <em data-toggle="tooltip"
title="Click to copy" title="Click to copy"
class="clipboard" class="clipboard"
data-clipboard-text="{{ alias_transfer_url }}"> data-clipboard-text="{{ alias_transfer_url }}">
{{ alias_transfer_url }} {{ alias_transfer_url }}
</em> </em>
<p class="mt-5"> <p class="mt-5">
Please copy the transfer URL. <strong>We won't be able to display it again</strong>. If you need to access it again you can generate a new URL. Please copy the transfer URL. <strong>We won't be able to display it again</strong>. If you need to access it again you can generate a new URL.
</p> </p>
<p class="mt-2"> <p class="mt-2">
This transfer URL is <strong>valid for 24 hours</strong>. If it hasn't been used by then it will be automatically disabled. This transfer URL is <strong>valid for 24 hours</strong>. If it hasn't been used by then it will be automatically disabled.
</p> </p>
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="remove"> <input type="hidden" name="form-name" value="remove">
<button class="btn btn-warning mt-2">Remove alias transfer URL</button> <button class="btn btn-warning mt-2">Remove alias transfer URL</button>
<div class="small-text"> <div class="small-text">If you don't want to share this alias anymore, you can remove the share URL.</div>
If you don't want to share this alias anymore, you can remove the share URL.
</div>
</form> </form>
{% else %} {% else %}
{% if link_active %} {% if link_active %}
<p class="alert alert-info"> <p class="alert alert-info">
You have an active transfer link. If you don't want to share this alias any more, please delete the link. You have an active transfer link. If you don't want to share this alias any more, please delete the link.
</p> </p>
@ -51,8 +42,8 @@
</form> </form>
{% else %} {% else %}
<p> <p>
In order to transfer ownership, In order to transfer ownership,
please create the <b>Share URL</b> 👇 and send it to the other person. please create the <b>Share URL</b> 👇 and send it to the other person.
</p> </p>
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="create"> <input type="hidden" name="form-name" value="create">
@ -60,17 +51,10 @@
</form> </form>
{% endif %} {% endif %}
{% endif %} {% endif %}
<p class="mt-5"> <p class="mt-5">
This person can then confirm the reception and become the owner of the alias. This person can then confirm the reception and become the owner of the alias.
</p> </p>
<div class="alert alert-danger"> <div class="alert alert-danger">After the confirmation, you can no longer use this alias.</div>
After the confirmation, you can no longer use this alias.
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,103 +1,84 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% block title %}
API Key
{% endblock %}
{% block title %}API Key{% endblock %}
{% set active_page = "api_key" %} {% set active_page = "api_key" %}
{% block head %}{% endblock %}
{% block head %}
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h1 class="h3"> API Keys </h1> <h1 class="h3">API Keys</h1>
<div class="alert alert-info"> <div class="alert alert-info">
When you log in on a SimpleLogin mobile app or browser extension, When you log in on a SimpleLogin mobile app or browser extension,
a new API Key is automatically created and stored on your device. a new API Key is automatically created and stored on your device.
It's usually named after the device where it was created, e.g. Samsung S8, John's iPhone, etc. It's usually named after the device where it was created, e.g. Samsung S8, John's iPhone, etc.
</div> </div>
<div class="alert alert-danger"> <div class="alert alert-danger">
API Keys should be kept secret and treated like passwords, they can be used to gain access to your account. API Keys should be kept secret and treated like passwords, they can be used to gain access to your account.
</div> </div>
<div class="row"> <div class="row">
{% for api_key in api_keys %} {% for api_key in api_keys %}
<div class="col-12 col-lg-6"> <div class="col-12 col-lg-6">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">{{ api_key.name or "N/A" }}</h5> <h5 class="card-title">{{ api_key.name or "N/A" }}</h5>
<h6 class="card-subtitle mb-2 text-muted"> <h6 class="card-subtitle mb-2 text-muted">
{% if api_key.last_used %} {% if api_key.last_used %}
Created {{ api_key.created_at | dt }}. Created {{ api_key.created_at | dt }}.
Used {{ api_key.times }} times. Used {{ api_key.times }} times.
Was last used {{ api_key.last_used | dt }}. Was last used {{ api_key.last_used | dt }}.
{% else %} {% else %}
Never used Never used
{% endif %} {% endif %}
</h6> </h6>
<div class="input-group"> <div class="input-group">
<input class="form-control" id="apikey-{{ api_key.id }}" readonly value="**********"> <input class="form-control"
id="apikey-{{ api_key.id }}"
readonly
value="**********">
</div> </div>
<br />
<br>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="delete"> <input type="hidden" name="form-name" value="delete">
<input type="hidden" name="api-key-id" value="{{ api_key.id }}"> <input type="hidden" name="api-key-id" value="{{ api_key.id }}">
<span class="card-link btn btn-link float-right text-danger delete-api-key"> <span class="card-link btn btn-link float-right text-danger delete-api-key">Delete</span>
Delete
</span>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% if api_keys|length > 0 %} {% if api_keys|length > 0 %}
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="delete-all"> <input type="hidden" name="form-name" value="delete-all">
<span class="delete btn btn-outline-danger delete-all-api-keys float-right"> <span class="delete btn btn-outline-danger delete-all-api-keys float-right">
Delete All &nbsp; &nbsp; <i class="fe fe-trash"></i> Delete All &nbsp; &nbsp; <i class="fe fe-trash"></i>
</span> </span>
</form> </form>
<br> <br />
{% endif %} {% endif %}
<hr />
<hr>
<form method="post"> <form method="post">
{{ new_api_key_form.csrf_token }} {{ new_api_key_form.csrf_token }}
<input type="hidden" name="form-name" value="create"> <input type="hidden" name="form-name" value="create">
<h2 class="h4">New API Key</h2> <h2 class="h4">New API Key</h2>
{{ new_api_key_form.name(class="form-control", placeholder="Chrome") }} {{ new_api_key_form.name(class="form-control", placeholder="Chrome") }}
{{ render_field_errors(new_api_key_form.name) }} {{ render_field_errors(new_api_key_form.name) }}
<div class="small-text">Name of the api key, e.g. where it will be used.</div> <div class="small-text">Name of the api key, e.g. where it will be used.</div>
<button class="btn btn-success mt-2">Create</button> <button class="btn btn-success mt-2">Create</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
$(".delete-api-key").on("click", function (e) { $(".delete-api-key").on("click", function (e) {
let that = $(this); let that = $(this);

View File

@ -1,81 +1,69 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "app" %} {% set active_page = "app" %}
{% block title %}Sign in with SimpleLogin apps{% endblock %}
{% block title %}
Sign in with SimpleLogin apps
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h1 class="h3"> Apps </h1> <h1 class="h3">Apps</h1>
<div class="small-text"> <div class="small-text">
List of websites/apps that you have used <b>Sign in with SimpleLogin</b> List of websites/apps that you have used <b>Sign in with SimpleLogin</b>
</div> </div>
</div> </div>
</div> </div>
<div class="row row-cards row-deck mt-4"> <div class="row row-cards row-deck mt-4">
<div class="col-12"> <div class="col-12">
<div class="card"> <div class="card">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-hover table-outline table-vcenter text-nowrap card-table"> <table class="table table-hover table-outline table-vcenter text-nowrap card-table">
<thead> <thead>
<tr> <tr>
<th> <th>App</th>
App <th>
</th> Info
<th> <i class="fe fe-help-circle"
Info data-toggle="tooltip"
<i class="fe fe-help-circle" data-toggle="tooltip" title="Info this app/website receives"></i>
title="Info this app/website receives"></i> </th>
</th> <th>
<th> First used
First used <i class="fe fe-help-circle"
<i class="fe fe-help-circle" data-toggle="tooltip" data-toggle="tooltip"
title="The first time you have used the 'Sign in with SimpleLogin' button on this app/website"></i> title="The first time you have used the 'Sign in with SimpleLogin' button on this app/website"></i>
</th> </th>
<th> <th>Actions</th>
Actions <!--<th class="text-center">Last used</th>-->
</th> </tr>
<!--<th class="text-center">Last used</th>-->
</tr>
</thead> </thead>
<tbody> <tbody>
{% for client_user in client_users %} {% for client_user in client_users %}
<tr>
<td>
{{ client_user.client.name }}
</td>
<td> <tr>
{% for scope, val in client_user.get_user_info().items() %} <td>{{ client_user.client.name }}</td>
<div> <td>
{% if scope == "email" %} {% for scope, val in client_user.get_user_info().items() %}
Email: <a href="mailto:{{ val }}">{{ val }}</a>
{% elif scope == "name" %}
Name: {{ val }}
{% endif %}
</div>
{% endfor %}
</td>
<td> <div>
{{ client_user.created_at | dt }} {% if scope == "email" %}
</td>
<td>
<form method="post">
<input type="hidden" name="client-user-id" value="{{ client_user.id }}">
<button class="btn btn-warning">
Remove
</button>
</form>
</td>
</tr>
{% endfor %}
Email:
<a href="mailto:{{ val }}">{{ val }}</a>
{% elif scope == "name" %}
Name: {{ val }}
{% endif %}
</div>
{% endfor %}
</td>
<td>{{ client_user.created_at | dt }}</td>
<td>
<form method="post">
<input type="hidden" name="client-user-id" value="{{ client_user.id }}">
<button class="btn btn-warning">Remove</button>
</form>
</td>
</tr>
{% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -1,63 +1,71 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "setting" %}
{% block title %}
Alias Batch Import
{% endblock %}
{% set active_page = "setting" %}
{% block title %}Alias Batch Import{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3">Alias Batch Import</h1> <h1 class="h3">Alias Batch Import</h1>
<div class="alert alert-primary"> <div class="alert alert-primary">
Only aliases created with <b>your verified domains</b> can be imported.<br> Only aliases created with <b>your verified domains</b> can be imported.
If mailboxes are set for an alias, they will only be linked if they already exist.<br> <br />
If mailboxes are set for an alias, they will only be linked if they already exist.
<br />
Please make sure to use the csv template file. Please make sure to use the csv template file.
</div> </div>
<p> <p>
The import can take several minutes. The import can take several minutes.
Please come back to this page to verify the import status. <br> Please come back to this page to verify the import status.
<br />
If an alias already exists, it won't be imported. If an alias already exists, it won't be imported.
</p> </p>
<a href="{{ url_for('static', filename='batch_import_template.csv') }}"
<a href="/static/batch_import_template.csv" download>Download CSV Template</a> download>Download CSV Template</a>
<hr />
<hr>
<form method="post" enctype="multipart/form-data" class="mt-4"> <form method="post" enctype="multipart/form-data" class="mt-4">
<input required type="file" <input required
type="file"
name="alias-file" name="alias-file"
accept=".csv" accept=".csv"
class="form-control-file"> class="form-control-file">
<label>Only <b>.csv</b> file is supported. </label> <br> <label>
Only <b>.csv</b> file is supported.
</label>
<br />
<button class="btn btn-success mt-2">Upload</button> <button class="btn btn-success mt-2">Upload</button>
</form> </form>
{% if batch_imports %} {% if batch_imports %}
<hr>
<hr />
<h2 class="h3 mt-7">Batch imports</h2> <h2 class="h3 mt-7">Batch imports</h2>
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th scope="col">Uploaded</th> <th scope="col">Uploaded</th>
<th scope="col">Number Alias Imported</th> <th scope="col">Number Alias Imported</th>
<th scope="col">Status</th> <th scope="col">Status</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for batch_import in batch_imports %} {% for batch_import in batch_imports %}
<tr>
<td>{{ batch_import.created_at | dt }}</td> <tr>
<td>{{ batch_import.nb_alias() }}</td> <td>{{ batch_import.created_at | dt }}</td>
<td>{% if batch_import.processed %} Processed ✅ {% else %} Pending {% endif %}</td> <td>{{ batch_import.nb_alias() }}</td>
</tr> <td>
{% endfor %} {% if batch_import.processed %}
Processed ✅
{% else %}
Pending
{% endif %}
</td>
</tr>
{% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,92 +1,86 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% block title %}
Billing
{% endblock %}
{% block head %}
{% endblock %}
{% block title %}Billing{% endblock %}
{% block head %}{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3 mb-5"> Billing </h1> <h1 class="h3 mb-5">Billing</h1>
{% if sub.cancelled %} {% if sub.cancelled %}
<p> <p>
You are on the <b>{{ sub.plan_name() }}</b> plan. <br> You are on the <b>{{ sub.plan_name() }}</b> plan.
<br />
You have canceled your subscription and it will end on {{ sub.next_bill_date.strftime("%Y-%m-%d") }} You have canceled your subscription and it will end on {{ sub.next_bill_date.strftime("%Y-%m-%d") }}
</p> </p>
<p class="alert alert-info"> <p class="alert alert-info">
If you change your mind you can subscribe again to SimpleLogin but If you change your mind you can subscribe again to SimpleLogin but
please note that this will be a completely please note that this will be a completely
new subscription and new subscription and
your payment method will be charged <b>immediately</b>. <br> your payment method will be charged <b>immediately</b>.
<br />
The period left in the current subscription isn't taken into account. The period left in the current subscription isn't taken into account.
<br> <br />
<a href="{{ url_for('dashboard.pricing') }}"
<a href="{{ url_for('dashboard.pricing') }}" class="btn btn-primary mt-2">Re-subscribe</a> class="btn btn-primary mt-2">Re-subscribe</a>
</p> </p>
{% else %} {% else %}
<p> <p>
You are on the <b>{{ sub.plan_name() }}</b> plan. Thank you very much for supporting You are on the <b>{{ sub.plan_name() }}</b> plan. Thank you very much for supporting
SimpleLogin. 🙌 <br> SimpleLogin. 🙌
<br />
The next billing cycle starts at {{ sub.next_bill_date.strftime("%Y-%m-%d") }}. The next billing cycle starts at {{ sub.next_bill_date.strftime("%Y-%m-%d") }}.
</p> </p>
<div class="mt-3"> <div class="mt-3">
Click here to update billing information on Paddle, our payment partner: <br> Click here to update billing information on Paddle, our payment partner:
<a class="btn btn-outline-success mt-2" href="{{ sub.update_url }}"> Update billing information </a> <br />
<a class="btn btn-outline-success mt-2" href="{{ sub.update_url }}">Update billing information</a>
</div> </div>
<hr> <hr />
<div class="mt-6"> <div class="mt-6">
<h4>Change Plan</h4> <h4>Change Plan</h4>
You can change the plan at any moment. <br> You can change the plan at any moment.
<br />
Please note that the new billing cycle starts instantly Please note that the new billing cycle starts instantly
i.e. you will be charged <b>immediately</b> the annual fee ($30) when switching from monthly plan or vice-versa i.e. you will be charged <b>immediately</b> the annual fee ($30) when switching from monthly plan or vice-versa
<b>without pro rata computation </b>. <br> <b>without pro rata computation </b>.
<br />
To change the plan you can also cancel the current one and subscribe a new one <b>by the end</b> of this plan. To change the plan you can also cancel the current one and subscribe a new one <b>by the end</b> of this plan.
{% if sub.plan == PlanEnum.yearly %} {% if sub.plan == PlanEnum.yearly %}
<form method="post" onsubmit="return confirm('This will change your current subscription plan, please confirm');">
<form method="post"
onsubmit="return confirm('This will change your current subscription plan, please confirm');">
<input type="hidden" name="form-name" value="change-monthly"> <input type="hidden" name="form-name" value="change-monthly">
<button class="btn btn-outline-primary mt-2">Change to Monthly Plan</button> <button class="btn btn-outline-primary mt-2">Change to Monthly Plan</button>
</form> </form>
{% else %} {% else %}
<form method="post" onsubmit="return confirm('This will change your current subscription plan, please confirm');"> <form method="post"
onsubmit="return confirm('This will change your current subscription plan, please confirm');">
<input type="hidden" name="form-name" value="change-yearly"> <input type="hidden" name="form-name" value="change-yearly">
<button class="btn btn-outline-primary mt-2">Change to Yearly Plan</button> <button class="btn btn-outline-primary mt-2">Change to Yearly Plan</button>
</form> </form>
{% endif %} {% endif %}
</div> </div>
<hr />
<hr>
<div> <div>
<h4>Cancel subscription</h4> <h4>Cancel subscription</h4>
Don't want to protect your inbox anymore? <br> Don't want to protect your inbox anymore?
<br />
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="cancel"> <input type="hidden" name="form-name" value="cancel">
<span class="cancel btn btn-outline-danger mt-2"> <span class="cancel btn btn-outline-danger mt-2">
Cancel subscription <i class="fe fe-alert-triangle text-danger"></i> Cancel subscription <i class="fe fe-alert-triangle text-danger"></i>
</span> </span>
</form> </form>
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script>
<script>
$(".cancel").on("click", function (e) { $(".cancel").on("click", function (e) {
let that = $(this); let that = $(this);
@ -111,4 +105,4 @@
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,28 +1,19 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}Block a sender{% endblock %}
{% block title %}
Block a sender
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3"> <h1 class="h3">Block sender</h1>
Block sender
</h1>
<p> <p>
You are about to block the sender <b>{{ contact.website_email }}</b> from sending emails to You are about to block the sender <b>{{ contact.website_email }}</b> from sending emails to
<b>{{ contact.alias.email }}</b> <b>{{ contact.alias.email }}</b>
</p> </p>
<form method="post"> <form method="post">
<button class="btn btn-warning">Confirm</button> <button class="btn btn-warning">Confirm</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,11 +1,7 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}Contact {{ contact.email }} - Alias {{ alias.email }}{% endblock %}
{% block title %}
Contact {{ contact.email }} - Alias {{ alias.email }}
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
@ -13,21 +9,24 @@
<h1 class="h3"> <h1 class="h3">
<nav aria-label="breadcrumb"> <nav aria-label="breadcrumb">
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="breadcrumb-item"><a <li class="breadcrumb-item">
href="{{ url_for('dashboard.alias_contact_manager', alias_id=alias.id) }}">{{ alias.email }}</a></li> <a href="{{ url_for('dashboard.alias_contact_manager', alias_id=alias.id) }}">{{ alias.email }}</a>
<li class="breadcrumb-item active" aria-current="page">{{ contact.email }} </li>
<li class="breadcrumb-item active" aria-current="page">
{{ contact.email }}
{% if contact.pgp_finger_print %} {% if contact.pgp_finger_print %}
<span class="cursor" data-toggle="tooltip" data-original-title="PGP Enabled">🗝</span>
<span class="cursor"
data-toggle="tooltip"
data-original-title="PGP Enabled">🗝</span>
{% endif %} {% endif %}
</li> </li>
</ol> </ol>
</nav> </nav>
</h1> </h1>
<div class="card"> <div class="card">
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="pgp"> <input type="hidden" name="form-name" value="pgp">
<div class="card-body"> <div class="card-body">
<div class="card-title"> <div class="card-title">
Pretty Good Privacy (PGP) Pretty Good Privacy (PGP)
@ -37,44 +36,30 @@
are <b>encrypted</b>. are <b>encrypted</b>.
</div> </div>
</div> </div>
{% if not current_user.is_premium() %} {% if not current_user.is_premium() %}
<div class="alert alert-danger" role="alert">
This feature is only available in premium plan.
</div>
{% endif %}
<div class="alert alert-danger" role="alert">This feature is only available in premium plan.</div>
{% endif %}
<div class="form-group"> <div class="form-group">
<label class="form-label">PGP Public Key</label> <label class="form-label">PGP Public Key</label>
<textarea name="pgp" {% if not current_user.is_premium() %} disabled {% endif %} class="form-control" rows=10 id="pgp-public-key" placeholder="-----BEGIN PGP PUBLIC KEY BLOCK-----">{{ contact.pgp_public_key or "" }}</textarea>
<textarea name="pgp"
{% if not current_user.is_premium() %} disabled {% endif %}
class="form-control" rows=10 id="pgp-public-key"
placeholder="-----BEGIN PGP PUBLIC KEY BLOCK-----">{{ contact.pgp_public_key or "" }}</textarea>
</div> </div>
<button class="btn btn-primary" name="action" {% if not current_user.is_premium() %}
<button class="btn btn-primary" name="action" disabled {% endif %} value="save">
{% if not current_user.is_premium() %} disabled {% endif %} Save
value="save">Save
</button> </button>
{% if contact.pgp_finger_print %} {% if contact.pgp_finger_print %}
<button class="btn btn-danger float-right" name="action" value="remove">Remove</button> <button class="btn btn-danger float-right" name="action" value="remove">Remove</button>
{% endif %} {% endif %}
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script src="/static/js/utils/drag-drop-into-text.js"></script>
<script> <script src="/static/js/utils/drag-drop-into-text.js"></script>
enableDragDropForPGPKeys('#pgp-public-key'); <script>enableDragDropForPGPKeys('#pgp-public-key');</script>
</script>
{% endblock %} {% endblock %}

View File

@ -1,70 +1,53 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block head %} {% block head %}
<script src="https://cdn.paddle.com/paddle/paddle.js"></script> <script src="https://cdn.paddle.com/paddle/paddle.js"></script>
<script> <script>
if (window.Paddle === undefined) { if (window.Paddle === undefined) {
console.log("cannot load Paddle from CDN"); console.log("cannot load Paddle from CDN");
document.write('<script src="/static/vendor/paddle.js"><\/script>') document.write('<script src="/static/vendor/paddle.js"><\/script>')
} }
</script> </script>
<style type="text/css">
<style type="text/css">
html.mvc__a.mvc__lot.mvc__of.mvc__classes.mvc__to.mvc__increase.mvc__the.mvc__odds.mvc__of.mvc__winning.mvc__specificity, html.mvc__a.mvc__lot.mvc__of.mvc__classes.mvc__to.mvc__increase.mvc__the.mvc__odds.mvc__of.mvc__winning.mvc__specificity > body { html.mvc__a.mvc__lot.mvc__of.mvc__classes.mvc__to.mvc__increase.mvc__the.mvc__odds.mvc__of.mvc__winning.mvc__specificity, html.mvc__a.mvc__lot.mvc__of.mvc__classes.mvc__to.mvc__increase.mvc__the.mvc__odds.mvc__of.mvc__winning.mvc__specificity > body {
position: static; position: static;
} }
</style> </style>
{% endblock %} {% endblock %}
{% block title %}Coupon{% endblock %}
{% block title %}
Coupon
{% endblock %}
{% block default_content %} {% block default_content %}
{% if can_use_coupon %}
<div class="card">
<div class="card-body">
<h1 class="h2">Coupon</h1>
<div class="mb-4">
Please enter the coupon here to upgrade your account or extend your current subscription.
</div>
<form method="post">
{{ coupon_form.csrf_token }}
{{ coupon_form.code(class="form-control", placeholder="Licence Code") }}
{{ render_field_errors(coupon_form.code) }}
<button class="btn btn-success mt-2">Apply</button>
</form>
</div>
</div>
{% endif %}
<div class="card">
<div class="card-body">
<h2>1-year coupon</h2>
<div class="mb-3">
You can buy a 1-year coupon that allows anyone to have the SimpleLogin premium for 1 year.
Can be an idea for a gift card :).
After the payment, the coupon will be sent to you by email.
</div>
<div class="alert alert-info">
The coupon must be used before {{ max_coupon_date.date().isoformat() }}
</div>
<a href="#!" class="paddle_button btn btn-primary" data-product="{{ PADDLE_COUPON_ID }}">Buy 1-year SimpleLogin
coupon</a>
</div>
</div>
{% if can_use_coupon %}
<div class="card">
<div class="card-body">
<h1 class="h2">Coupon</h1>
<div class="mb-4">Please enter the coupon here to upgrade your account or extend your current subscription.</div>
<form method="post">
{{ coupon_form.csrf_token }}
{{ coupon_form.code(class="form-control", placeholder="Licence Code") }}
{{ render_field_errors(coupon_form.code) }}
<button class="btn btn-success mt-2">Apply</button>
</form>
</div>
</div>
{% endif %}
<div class="card">
<div class="card-body">
<h2>1-year coupon</h2>
<div class="mb-3">
You can buy a 1-year coupon that allows anyone to have the SimpleLogin premium for 1 year.
Can be an idea for a gift card :).
After the payment, the coupon will be sent to you by email.
</div>
<div class="alert alert-info">The coupon must be used before {{ max_coupon_date.date().isoformat() }}</div>
<a href="#!" class="paddle_button btn btn-primary" data-product="{{ PADDLE_COUPON_ID }}">
Buy 1-year SimpleLogin
coupon
</a>
</div>
</div>
{% endblock %} {% endblock %}
{% block script %}<script type="text/javascript">Paddle.Setup({vendor: {{PADDLE_VENDOR_ID } }});</script>{% endblock %}
{% block script %}
<script type="text/javascript">
Paddle.Setup({vendor: {{PADDLE_VENDOR_ID}}});
</script>
{% endblock %}

View File

@ -1,34 +1,33 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}Custom Alias{% endblock %}
{% block title %}
Custom Alias
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3">New Custom Alias</h1> <h1 class="h3">New Custom Alias</h1>
{% if user_custom_domains|length == 0 and not DISABLE_ALIAS_SUFFIX %} {% if user_custom_domains|length == 0 and not DISABLE_ALIAS_SUFFIX %}
<div class="row"> <div class="row">
<div class="col p-1"> <div class="col p-1">
<div class="alert alert-primary" role="alert"> <div class="alert alert-primary" role="alert">
You might notice a random word after the dot(<em>.</em>) in the alias. You might notice a random word after the dot(<em>.</em>) in the alias.
This part is to avoid a person taking all the "nice" aliases like This part is to avoid a person taking all the "nice" aliases like
<b>hello@{{ FIRST_ALIAS_DOMAIN }}</b>, <b>hello@{{ FIRST_ALIAS_DOMAIN }}</b>,
<b>me@{{ FIRST_ALIAS_DOMAIN }}</b>, etc. <br> <b>me@{{ FIRST_ALIAS_DOMAIN }}</b>, etc.
If you add your own domain, this restriction is removed, and you can fully customize the alias. <br> <br />
If you add your own domain, this restriction is removed, and you can fully customize the alias.
<br />
</div> </div>
</div> </div>
</div> </div>
{% endif %} {% endif %}
<form method="post" data-parsley-validate> <form method="post" data-parsley-validate>
<div class="row mb-2"> <div class="row mb-2">
<div class="col-sm-6 mb-1 p-1" style="min-width: 4em"> <div class="col-sm-6 mb-1 p-1" style="min-width: 4em">
<input name="prefix" class="form-control" <input name="prefix"
class="form-control"
id="prefix" id="prefix"
type="text" type="text"
data-parsley-pattern="[0-9a-z-_.]{1,}" data-parsley-pattern="[0-9a-z-_.]{1,}"
@ -36,30 +35,26 @@
data-parsley-error-message="Only lowercase letters, dots, numbers, dashes (-) and underscores (_) are currently supported." data-parsley-error-message="Only lowercase letters, dots, numbers, dashes (-) and underscores (_) are currently supported."
maxlength="40" maxlength="40"
placeholder="Alias prefix, for example newsletter.com-123_xyz" placeholder="Alias prefix, for example newsletter.com-123_xyz"
autofocus required> autofocus
required>
</div> </div>
<div class="col-sm-6 p-1"> <div class="col-sm-6 p-1">
<select class="form-control" name="signed-alias-suffix"> <select class="form-control" name="signed-alias-suffix">
{% for suffix_info in alias_suffixes_with_signature %} {% for suffix_info in alias_suffixes_with_signature %}
{% set alias_suffix = suffix_info[0] %} {% set alias_suffix = suffix_info[0] %}
<option value="{{ suffix_info[1] }}" <option value="{{ suffix_info[1] }}" {% if alias_suffix.is_premium %}
{% if alias_suffix.is_premium %} title="Only available to Premium accounts" {% elif not alias_suffix.is_custom and at_least_a_premium_domain %} title="Available to all accounts" {% endif %}>
title="Only available to Premium accounts"
{% elif not alias_suffix.is_custom and at_least_a_premium_domain %}
title="Available to all accounts"
{% endif %}
>
{% if alias_suffix.is_custom %} {% if alias_suffix.is_custom %}
{% if alias_suffix.mx_verified %} {% if alias_suffix.mx_verified %}
{{ alias_suffix.suffix }} (your domain) {{ alias_suffix.suffix }} (your domain)
{% else %} {% else %}
{{ alias_suffix.suffix }} (your domain, not MX verified yet) {{ alias_suffix.suffix }} (your domain, not MX verified yet)
{% endif %} {% endif %}
{% else %} {% else %}
{% if alias_suffix.is_premium %} {% if alias_suffix.is_premium %}
{{ alias_suffix.suffix }} (Premium domain) {{ alias_suffix.suffix }} (Premium domain)
{% else %} {% else %}
{{ alias_suffix.suffix }} (Public domain) {{ alias_suffix.suffix }} (Public domain)
@ -70,24 +65,25 @@
</select> </select>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<div class="col p-1"> <div class="col p-1">
<select data-width="100%" <select data-width="100%"
class="mailbox-select" id="mailboxes" multiple name="mailboxes" required> class="mailbox-select"
id="mailboxes"
multiple
name="mailboxes"
required>
{% for mailbox in mailboxes %} {% for mailbox in mailboxes %}
<option value="{{ mailbox.id }}" {% if mailbox.id == current_user.default_mailbox_id %}
selected {% endif %}> <option value="{{ mailbox.id }}"
{% if mailbox.id == current_user.default_mailbox_id %} selected{% endif %}>
{{ mailbox.email }} {{ mailbox.email }}
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
<div class="small-text"> <div class="small-text">The mailbox(es) that owns this alias.</div>
The mailbox(es) that owns this alias.
</div>
</div> </div>
</div> </div>
<div class="row mb-2"> <div class="row mb-2">
<div class="col p-1"> <div class="col p-1">
<textarea name="note" <textarea name="note"
@ -96,7 +92,6 @@
placeholder="Note, can be anything to help you remember why you created this alias. This field is optional."></textarea> placeholder="Note, can be anything to help you remember why you created this alias. This field is optional."></textarea>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col p-1"> <div class="col p-1">
<button type="submit" id="create" class="btn btn-primary mt-1">Create</button> <button type="submit" id="create" class="btn btn-primary mt-1">Create</button>
@ -105,10 +100,9 @@
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
$('.mailbox-select').multipleSelect(); $('.mailbox-select').multipleSelect();
@ -137,6 +131,5 @@
that.closest("form").submit(); that.closest("form").submit();
}) })
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,24 +1,26 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "custom_domain" %} {% set active_page = "custom_domain" %}
{% block title %}Custom Domains{% endblock %}
{% block title %} {% block head %}{% endblock %}
Custom Domains
{% endblock %}
{% block head %}
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h1 class="h3"> Custom Domains <h1 class="h3">
<a class="ml-3 text-info" style="font-size: 12px" data-toggle="collapse" href="#howtouse" role="button" Custom Domains
aria-expanded="false" aria-controls="collapseExample"> <a class="ml-3 text-info"
style="font-size: 12px"
data-toggle="collapse"
href="#howtouse"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
How to use <i class="fe fe-chevrons-down"></i> How to use <i class="fe fe-chevrons-down"></i>
</a> </a>
</h1> </h1>
{% if not current_user.is_premium() %} {% if not current_user.is_premium() %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
This feature is only available on Premium plan. This feature is only available on Premium plan.
<a href="{{ URL }}/dashboard/pricing" target="_blank" rel="noopener"> <a href="{{ URL }}/dashboard/pricing" target="_blank" rel="noopener">
@ -26,86 +28,74 @@
</a> </a>
</div> </div>
{% endif %} {% endif %}
<div class="alert alert-primary collapse {% if not custom_domains %} show{% endif %}"
<div class="alert alert-primary collapse {% if not custom_domains %} show {% endif %}" id="howtouse" role="alert"> id="howtouse"
By adding your domain, you can create aliases like <b>hi@my-domain.com</b> <br> role="alert">
By adding your domain, you can create aliases like <b>hi@my-domain.com</b>
<br />
You can also enable <b>catch-all</b> to create aliases on-the-fly: You can also enable <b>catch-all</b> to create aliases on-the-fly:
simply use <b>anything@my-domain.com</b> and it'll be created when simply use <b>anything@my-domain.com</b> and it'll be created when
it receives an email. it receives an email.
</div> </div>
<div class="row"> <div class="row">
{% for custom_domain in custom_domains %} {% for custom_domain in custom_domains %}
<div class="col-12 col-lg-6"> <div class="col-12 col-lg-6">
<div class="card" style=""> <div class="card" style="">
<div class="card-body"> <div class="card-body">
<h5 class="card-title"> <h5 class="card-title">
<a href="{{ url_for('dashboard.domain_detail', custom_domain_id=custom_domain.id) }}">{{ custom_domain.domain }}</a> <a href="{{ url_for('dashboard.domain_detail', custom_domain_id=custom_domain.id) }}">{{ custom_domain.domain }}</a>
{% if custom_domain.ownership_verified and not custom_domain.verified %} {% if custom_domain.ownership_verified and not custom_domain.verified %}
<a href="{{ url_for('dashboard.domain_detail_dns', custom_domain_id=custom_domain.id,
_anchor='dns-setup') }}" class="btn btn-info btn-sm"> <a href="{{ url_for('dashboard.domain_detail_dns', custom_domain_id=custom_domain.id, _anchor='dns-setup') }}"
class="btn btn-info btn-sm">
Ownership verified. Setup the DNS Ownership verified. Setup the DNS
</a> </a>
{% elif custom_domain.ownership_verified and custom_domain.verified %} {% elif custom_domain.ownership_verified and custom_domain.verified %}
<span class="badge badge-success">Domain ready</span> <span class="badge badge-success">Domain ready</span>
<!-- custom_domain.ownership_verified is False --> <!-- custom_domain.ownership_verified is False -->
{% else %} {% else %}
<a href="{{ url_for('dashboard.domain_detail_dns', custom_domain_id=custom_domain.id, <a href="{{ url_for('dashboard.domain_detail_dns', custom_domain_id=custom_domain.id, _anchor='ownership-form') }}"
_anchor='ownership-form') }}" class="btn btn-warning btn-sm" role="button"> class="btn btn-warning btn-sm"
role="button">
Verify domain ownership Verify domain ownership
</a> </a>
{% endif %} {% endif %}
</h5> </h5>
<h6 class="card-subtitle mb-4 text-muted"> <h6 class="card-subtitle mb-4 text-muted">
Created {{ custom_domain.created_at | dt }} <br> Created {{ custom_domain.created_at | dt }}
<br />
<span class="font-weight-bold">{{ custom_domain.nb_alias() }}</span> aliases. <span class="font-weight-bold">{{ custom_domain.nb_alias() }}</span> aliases.
<br> <br />
</h6> </h6>
<a href="{{ url_for('dashboard.domain_detail', custom_domain_id=custom_domain.id) }}"
<a href="{{ url_for('dashboard.domain_detail', custom_domain_id=custom_domain.id) }}" class="mt-3"> class="mt-3">Details ➡</a>
Details ➡
</a>
</div> </div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<form method="post" class="mt-2"> <form method="post" class="mt-2">
{{ new_custom_domain_form.csrf_token }} {{ new_custom_domain_form.csrf_token }}
<input type="hidden" name="form-name" value="create"> <input type="hidden" name="form-name" value="create">
<h2 class="h4">New Domain</h2> <h2 class="h4">New Domain</h2>
{{ new_custom_domain_form.domain(class="form-control", placeholder="my-domain.com", maxlength=128) }} {{ new_custom_domain_form.domain(class="form-control", placeholder="my-domain.com", maxlength=128) }}
{{ render_field_errors(new_custom_domain_form.domain) }} {{ render_field_errors(new_custom_domain_form.domain) }}
<div class="small-text"> <div class="small-text">
Please use full path domain, for example <b>my-domain.com</b> Please use full path domain, for example <b>my-domain.com</b>
or <b>my-subdomain.my-domain.com</b> if you are using a subdomain. or <b>my-subdomain.my-domain.com</b> if you are using a subdomain.
</div> </div>
<button class="btn btn-primary mt-2">Create</button> <button class="btn btn-primary mt-2">Create</button>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %}<script>$('.mailbox-select').multipleSelect();</script>{% endblock %}
{% block script %}
<script>
$('.mailbox-select').multipleSelect();
</script>
{% endblock %}

View File

@ -1,11 +1,9 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "setting" %} {% set active_page = "setting" %}
{% block title %} {% block title %}Delete account{% endblock %}
Delete account
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="h2">Account Deletion</div> <div class="h2">Account Deletion</div>
@ -13,7 +11,6 @@
Once an account is deleted, it can't be restored. Once an account is deleted, it can't be restored.
All its records (aliases, domains, settings, etc.) are immediately deleted. All its records (aliases, domains, settings, etc.) are immediately deleted.
</div> </div>
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="delete-account"> <input type="hidden" name="form-name" value="delete-account">
<span class="delete-account btn btn-outline-danger">Delete account</span> <span class="delete-account btn btn-outline-danger">Delete account</span>
@ -21,8 +18,8 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
$(".delete-account").on("click", function (e) { $(".delete-account").on("click", function (e) {
let that = $(this); let that = $(this);
@ -50,4 +47,4 @@
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,198 +1,176 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "directory" %} {% set active_page = "directory" %}
{% block title %}Directory{% endblock %}
{% block title %}
Directory
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h1 class="h3"> Directories <h1 class="h3">
<a class="ml-3 text-info" style="font-size: 12px" data-toggle="collapse" href="#howtouse" role="button" Directories
aria-expanded="false" aria-controls="collapseExample"> <a class="ml-3 text-info"
style="font-size: 12px"
data-toggle="collapse"
href="#howtouse"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
How to use <i class="fe fe-chevrons-down"></i> How to use <i class="fe fe-chevrons-down"></i>
</a> </a>
</h1> </h1>
{% if not current_user.is_premium() %} {% if not current_user.is_premium() %}
<div class="alert alert-danger" role="alert">
This feature is only available in premium plan.
</div>
{% endif %}
<div class="alert alert-primary collapse {% if not dirs %} show {% endif %}" id="howtouse" role="alert"> <div class="alert alert-danger" role="alert">This feature is only available in premium plan.</div>
{% endif %}
<div class="alert alert-primary collapse {% if not dirs %} show{% endif %}"
id="howtouse"
role="alert">
<div> <div>
Directory allows you to create aliases <b>on the fly</b>. Directory allows you to create aliases <b>on the fly</b>.
</div> </div>
<div class="mt-2 pb-2"> <div class="mt-2 pb-2">
1⃣ Pick a name for your directory, says <em>my_directory</em> <br> 1⃣ Pick a name for your directory, says <em>my_directory</em>
<br />
2⃣ Quickly use one of the following formats to create an alias on-the-fly <b>without creating this alias 2⃣ Quickly use one of the following formats to create an alias on-the-fly <b>without creating this alias
beforehand</b> beforehand</b>
</div>
<div class="pl-3 py-2 bg-secondary">
<em>my_directory/<b>anything</b>@{{ FIRST_ALIAS_DOMAIN }}</em> or <br>
<em>my_directory+<b>anything</b>@{{ FIRST_ALIAS_DOMAIN }}</em> or <br>
<em>my_directory#<b>anything</b>@{{ FIRST_ALIAS_DOMAIN }}</em> <br>
</div>
<em><b>anything</b></em> is any string composed of lowercase characters. <br>
You can find more info on directory on our <a href="https://simplelogin.io/blog/alias-directory/">blog post</a>.
<div class="mt-2">
You can use this feature on the following domains:
{% for alias_domain in ALIAS_DOMAINS %}
<div class="font-weight-bold">{{ alias_domain }} </div>
{% endfor %}
</div>
<div class="h4 text-primary mt-3">
The alias will be created the first time it receives an email.
</div>
</div> </div>
<div class="pl-3 py-2 bg-secondary">
<em>my_directory/<b>anything</b>@{{ FIRST_ALIAS_DOMAIN }}</em> or
<br />
<em>my_directory+<b>anything</b>@{{ FIRST_ALIAS_DOMAIN }}</em> or
<br />
<em>my_directory#<b>anything</b>@{{ FIRST_ALIAS_DOMAIN }}</em>
<br />
</div>
<em><b>anything</b></em> is any string composed of lowercase characters.
<br />
You can find more info on directory on our
<a href="https://simplelogin.io/blog/alias-directory/">blog post</a>
.
<div class="mt-2">
You can use this feature on the following domains:
{% for alias_domain in ALIAS_DOMAINS %}<div class="font-weight-bold">{{ alias_domain }}</div>{% endfor %}
</div>
<div class="h4 text-primary mt-3">
The alias will be created the first time it receives an email.
</div>
</div>
<div class="row">
{% for dir in dirs %}
<div class="row"> <div class="col-12 col-lg-6">
{% for dir in dirs %} <div class="card" style="">
<div class="col-12 col-lg-6"> <div class="card-body">
<div class="card" style=""> <h5 class="card-title">
<div class="card-body"> <div class="d-flex">
<h5 class="card-title"> {{ dir.name }}
<div class="d-flex"> <form method="post">
{{ dir.name }} <input type="hidden" name="form-name" value="toggle-directory">
<form method="post">
<input type="hidden" name="form-name" value="toggle-directory">
<input type="hidden" name="dir-id" value="{{ dir.id }}">
<label class="custom-switch cursor" style="padding-left: 1rem"
data-toggle="tooltip"
{% if dir.disabled %}
title="Enable directory on-the-fly alias creation"
{% else %}
title="Disable directory on-the-fly alias creation"
{% endif %}
>
<input type="checkbox" class="custom-switch-input" name="dir-status"
{{ "" if dir.disabled else "checked" }}>
<span class="custom-switch-indicator"></span>
</label>
</form>
</div>
</h5>
<h6 class="card-subtitle mb-2 text-muted">
{% if dir.disabled %}
<div class="mb-3">
⚠️ On-the-fly alias creation is disabled, you can't create new aliases with this directory.
</div>
{% endif %}
Created {{ dir.created_at | dt }} <br>
<span class="font-weight-bold">{{ dir.nb_alias() }}</span> aliases.
<br><br>
<b>Mailboxes:</b> <i class="fe fe-info" data-toggle="tooltip"
title="Aliases created with this directory are automatically owned by these mailboxes"></i>
<br>
{% set dir_mailboxes=dir.mailboxes %}
<form method="post" class="mt-2">
<input type="hidden" name="form-name" value="update">
<input type="hidden" name="dir-id" value="{{ dir.id }}"> <input type="hidden" name="dir-id" value="{{ dir.id }}">
<label class="custom-switch cursor" style="padding-left: 1rem" data-toggle="tooltip" {% if dir.disabled %}
<select data-width="100%" required title="Enable directory on-the-fly alias creation" {% else %} title="Disable directory on-the-fly alias creation" {% endif %}>
class="mailbox-select" multiple name="mailbox_ids"> <input type="checkbox" class="custom-switch-input" name="dir-status" {{ "" if dir.disabled else "checked" }}>
{% for mailbox in mailboxes %} <span class="custom-switch-indicator"></span>
<option value="{{ mailbox.id }}" {% if mailbox in dir_mailboxes %} </label>
selected {% endif %}>
{{ mailbox.email }}
</option>
{% endfor %}
</select>
<button class="mt-2 btn btn-outline-primary btn-sm">Update</button>
</form> </form>
</h6>
</div>
<div class="card-footer p-0">
<div class="row">
<div class="col">
<form method="post">
<input type="hidden" name="form-name" value="delete">
<input type="hidden" class="dir-name" value="{{ dir.name }}">
<input type="hidden" name="dir-id" value="{{ dir.id }}">
<span class="card-link btn btn-link float-right text-danger delete-dir">
Delete
</span>
</form>
</div>
</div> </div>
</h5>
<h6 class="card-subtitle mb-2 text-muted">
{% if dir.disabled %}
<div class="mb-3">⚠️ On-the-fly alias creation is disabled, you can't create new aliases with this directory.</div>
{% endif %}
Created {{ dir.created_at | dt }}
<br />
<span class="font-weight-bold">{{ dir.nb_alias() }}</span> aliases.
<br />
<br />
<b>Mailboxes:</b> <i class="fe fe-info"
data-toggle="tooltip"
title="Aliases created with this directory are automatically owned by these mailboxes"></i>
<br />
{% set dir_mailboxes=dir.mailboxes %}
<form method="post" class="mt-2">
<input type="hidden" name="form-name" value="update">
<input type="hidden" name="dir-id" value="{{ dir.id }}">
<select data-width="100%"
required
class="mailbox-select"
multiple
name="mailbox_ids">
{% for mailbox in mailboxes %}
<option value="{{ mailbox.id }}"
{% if mailbox in dir_mailboxes %} selected{% endif %}>
{{ mailbox.email }}
</option>
{% endfor %}
</select>
<button class="mt-2 btn btn-outline-primary btn-sm">Update</button>
</form>
</h6>
</div>
<div class="card-footer p-0">
<div class="row">
<div class="col">
<form method="post">
<input type="hidden" name="form-name" value="delete">
<input type="hidden" class="dir-name" value="{{ dir.name }}">
<input type="hidden" name="dir-id" value="{{ dir.id }}">
<span class="card-link btn btn-link float-right text-danger delete-dir">Delete</span>
</form>
</div>
</div> </div>
</div> </div>
</div> </div>
{% endfor %} </div>
</div> {% endfor %}
</div>
<div class="row {% if current_user.directory_quota <= 0 %} disabled-content{% endif %}">
<div class="col">
<div class="card">
<div class="card-body">
<form method="post" class="mt-2">
{{ new_dir_form.csrf_token }}
<input type="hidden" name="form-name" value="create">
<h2 class="h4 mb-1">New Directory</h2>
<div class="small-text mb-4">You can create up to {{ current_user.directory_quota }} directories.</div>
{{ new_dir_form.name(class="form-control", placeholder="my-directory",
pattern="[0-9a-z-_]{3,}",
title="Only letter, number, dash (-), underscore (_) can be used. Directory name must be at least 3 characters.") }}
{{ render_field_errors(new_dir_form.name) }}
<div class="small-text">
Directory name must be at least 3 characters.
Only lowercase letters, numbers, dashes (-) and underscores (_) are currently supported.
</div>
<div class="mt-3 small-text alert alert-info">
By default, aliases created with directory are "owned" by your default
mailbox <b>{{ current_user.default_mailbox.email }}</b>.
<br />
You can however choose the mailbox(es) that new alias automatically belongs to by setting this below
option.
</div>
<select data-width="100%" class="mailbox-select" multiple name="mailbox_ids">
{% for mailbox in mailboxes %}
<div class="row {% if current_user.directory_quota <= 0 %} disabled-content {% endif %}"> <option value="{{ mailbox.id }}"
<div class="col"> {% if mailbox.id == current_user.default_mailbox_id %} selected{% endif %}>
<div class="card"> {{ mailbox.email }}
<div class="card-body"> </option>
{% endfor %}
<form method="post" class="mt-2"> </select>
{{ new_dir_form.csrf_token }} <button id="btn-create-directory" class="btn btn-primary mt-2">Create</button>
<input type="hidden" name="form-name" value="create"> </form>
<h2 class="h4 mb-1">New Directory</h2>
<div class="small-text mb-4">
You can create up to {{ current_user.directory_quota }} directories.
</div>
{{ new_dir_form.name(class="form-control", placeholder="my-directory",
pattern="[0-9a-z-_]{3,}",
title="Only letter, number, dash (-), underscore (_) can be used. Directory name must be at least 3 characters.") }}
{{ render_field_errors(new_dir_form.name) }}
<div class="small-text">
Directory name must be at least 3 characters.
Only lowercase letters, numbers, dashes (-) and underscores (_) are currently supported.
</div>
<div class="mt-3 small-text alert alert-info">
By default, aliases created with directory are "owned" by your default
mailbox <b>{{ current_user.default_mailbox.email }}</b>. <br>
You can however choose the mailbox(es) that new alias automatically belongs to by setting this below
option.
</div>
<select data-width="100%"
class="mailbox-select" multiple name="mailbox_ids">
{% for mailbox in mailboxes %}
<option value="{{ mailbox.id }}" {% if mailbox.id == current_user.default_mailbox_id %}
selected {% endif %}>
{{ mailbox.email }}
</option>
{% endfor %}
</select>
<button id="btn-create-directory" class="btn btn-primary mt-2">Create</button>
</form>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
$(".delete-dir").on("click", function (e) { $(".delete-dir").on("click", function (e) {
let directory = $(this).parent().find(".dir-name").val(); let directory = $(this).parent().find(".dir-name").val();

View File

@ -1,55 +1,46 @@
{% extends 'dashboard/domain_detail/base.html' %} {% extends "dashboard/domain_detail/base.html" %}
{% set domain_detail_page = "auto_create" %} {% set domain_detail_page = "auto_create" %}
{% block title %}{{ custom_domain.domain }} Auto Create Rules{% endblock %}
{% block title %}
{{ custom_domain.domain }} Auto Create Rules
{% endblock %}
{% block domain_detail_content %} {% block domain_detail_content %}
<h1 class="h2 mb-1"> {{ custom_domain.domain }} auto create alias rules </h1>
<h1 class="h2 mb-1">{{ custom_domain.domain }} auto create alias rules</h1>
<div> <div>
<span class="badge badge-info">Advanced</span> <span class="badge badge-info">Advanced</span>
<span class="badge badge-warning">Beta</span> <span class="badge badge-warning">Beta</span>
</div> </div>
{% if custom_domain.catch_all %} {% if custom_domain.catch_all %}
<div class="alert alert-warning mt-3">
Rules are ineffective when catch-all is enabled.
</div>
{% endif %}
<div class="{% if custom_domain.catch_all %} disabled-content {% endif %}"> <div class="alert alert-warning mt-3">Rules are ineffective when catch-all is enabled.</div>
{% endif %}
<div class="{% if custom_domain.catch_all %} disabled-content{% endif %}">
<div class="mt-3 mb-2"> <div class="mt-3 mb-2">
For a greater control than a simple catch-all, you can define a set of <b>rules</b> to auto create aliases. <br> For a greater control than a simple catch-all, you can define a set of <b>rules</b> to auto create aliases.
<br />
A rule is based on a regular expression (<b>regex</b>): if an alias <b>fully</b> matches the expression, A rule is based on a regular expression (<b>regex</b>): if an alias <b>fully</b> matches the expression,
it'll be automatically created. it'll be automatically created.
</div> </div>
<div class="alert alert-info"> <div class="alert alert-info">
Only the <b>local</b> part of the alias (i.e. <b>@{{ custom_domain.domain }}</b> is ignored) during the Only the <b>local</b> part of the alias (i.e. <b>@{{ custom_domain.domain }}</b> is ignored) during the
regex test. regex test.
</div> </div>
<div class="alert alert-info">When there are several rules, rules will be evaluated by their order.</div>
<div class="alert alert-info">
When there are several rules, rules will be evaluated by their order.
</div>
{% if custom_domain.auto_create_rules | length > 0 %} {% if custom_domain.auto_create_rules | length > 0 %}
<div class="mt-2" id="rule-list"> <div class="mt-2" id="rule-list">
{% for auto_create_rule in custom_domain.auto_create_rules %} {% for auto_create_rule in custom_domain.auto_create_rules %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
Order: <b>{{ auto_create_rule.order }}</b> <br> Order: <b>{{ auto_create_rule.order }}</b>
<br />
<input readonly value="{{ auto_create_rule.regex }}" class="form-control"> <input readonly value="{{ auto_create_rule.regex }}" class="form-control">
New alias will belong to New alias will belong to
{% for mailbox in auto_create_rule.mailboxes %} {% for mailbox in auto_create_rule.mailboxes %}
<b>{{ mailbox.email }}</b> <b>{{ mailbox.email }}</b>
{% if not loop.last %},{% endif %} {% if not loop.last %},{% endif %}
{% endfor %} {% endfor %}
<form method="post" class="mt-2"> <form method="post" class="mt-2">
<input type="hidden" name="form-name" value="delete-auto-create-rule"> <input type="hidden" name="form-name" value="delete-auto-create-rule">
<input type="hidden" name="rule-id" value="{{ auto_create_rule.id }}"> <input type="hidden" name="rule-id" value="{{ auto_create_rule.id }}">
@ -60,112 +51,90 @@
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
<div class="mt-2" id="new-rule"> <div class="mt-2" id="new-rule">
<hr> <hr />
<h3>New rule </h3> <h3>New rule</h3>
<form method="post" action="#rule-list" data-parsley-validate> <form method="post" action="#rule-list" data-parsley-validate>
<input type="hidden" name="form-name" value="create-auto-create-rule"> <input type="hidden" name="form-name" value="create-auto-create-rule">
{{ new_auto_create_rule_form.csrf_token }} {{ new_auto_create_rule_form.csrf_token }}
<div class="form-group"> <div class="form-group">
<label>Regex</label> <label>Regex</label>
{{ new_auto_create_rule_form.regex(class="form-control", {{ new_auto_create_rule_form.regex(class="form-control",
placeholder="prefix.*" placeholder="prefix.*"
) }} ) }}
{{ render_field_errors(new_auto_create_rule_form.regex) }} {{ render_field_errors(new_auto_create_rule_form.regex) }}
<div class="small-text"> <div class="small-text">
For example, if you want aliases that starts with <b>prefix</b> to be automatically created, you can set For example, if you want aliases that starts with <b>prefix</b> to be automatically created, you can set
the the
regex to <em data-toggle="tooltip" regex to <em data-toggle="tooltip"
title="Click to copy" title="Click to copy"
class="clipboard" class="clipboard"
data-clipboard-text="prefix.*">prefix.*</em> data-clipboard-text="prefix.*">prefix.*</em>
<br> <br />
If you want aliases that ends with <b>suffix</b> to be automatically created, you can use the regex If you want aliases that ends with <b>suffix</b> to be automatically created, you can use the regex
<em data-toggle="tooltip" <em data-toggle="tooltip"
title="Click to copy" title="Click to copy"
class="clipboard" class="clipboard"
data-clipboard-text=".*suffix">.*suffix</em> data-clipboard-text=".*suffix">.*suffix</em>
<br> <br />
To test out regex, we recommend using regex tester tool like To test out regex, we recommend using regex tester tool like
<a href="https://regex101.com" target="_blank">https://regex101.com↗</a> <a href="https://regex101.com" target="_blank">https://regex101.com↗</a>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Order</label> <label>Order</label>
{{ new_auto_create_rule_form.order(class="form-control", placeholder="10", min=1, value=1, type="number") }} {{ new_auto_create_rule_form.order(class="form-control", placeholder="10", min=1, value=1, type="number") }}
{{ render_field_errors(new_auto_create_rule_form.order) }} {{ render_field_errors(new_auto_create_rule_form.order) }}
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="flex-grow-1 mr-2"> <div class="flex-grow-1 mr-2">
<select data-width="100%" required <select data-width="100%"
class="mailbox-select" multiple name="mailbox_ids"> required
class="mailbox-select"
multiple
name="mailbox_ids">
{% for mailbox in mailboxes %} {% for mailbox in mailboxes %}
<option value="{{ mailbox.id }}" {% if mailbox.id == current_user.default_mailbox_id %}
selected {% endif %}> <option value="{{ mailbox.id }}"
{% if mailbox.id == current_user.default_mailbox_id %} selected{% endif %}>
{{ mailbox.email }} {{ mailbox.email }}
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
</div> </div>
<button class="btn btn-primary mt-2">Create</button> <button class="btn btn-primary mt-2">Create</button>
</form> </form>
</div> </div>
<div id="debug-zone"> <div id="debug-zone">
<hr> <hr />
<h3>Debug Zone</h3> <h3>Debug Zone</h3>
<p>You can test whether an alias will be automatically created given the rules above </p> <p>
<div class="alert alert-info"> You can test whether an alias will be automatically created given the rules above
No worries, no alias will be created during the test :) </p>
</div> <div class="alert alert-info">No worries, no alias will be created during the test :)</div>
<form method="post" action="#debug-zone"> <form method="post" action="#debug-zone">
<input type="hidden" name="form-name" value="test-auto-create-rule"> <input type="hidden" name="form-name" value="test-auto-create-rule">
{{ auto_create_test_form.csrf_token }} {{ auto_create_test_form.csrf_token }}
<div class="d-flex"> <div class="d-flex">
<div class="form-group d-flex"> <div class="form-group d-flex">
{{ auto_create_test_form.local(class="form-control", type="text", placeholder="local", value=auto_create_test_local) }} {{ auto_create_test_form.local(class="form-control", type="text", placeholder="local", value=auto_create_test_local) }}
{{ render_field_errors(auto_create_test_form.local) }} {{ render_field_errors(auto_create_test_form.local) }}
<b class="pt-2 ml-1">@{{ custom_domain.domain }}</b> <b class="pt-2 ml-1">@{{ custom_domain.domain }}</b>
</div> </div>
<div>
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<button class="btn btn-outline-primary">Test</button> <button class="btn btn-outline-primary">Test</button>
</div> </div>
</form> </form>
{% if auto_create_test_result %} {% if auto_create_test_result %}
<div class="alert {% if auto_create_test_passed %} alert-success {% else %} alert-warning {% endif %}">
<div class="alert {% if auto_create_test_passed %}
alert-success {% else %} alert-warning {% endif %}">
{{ auto_create_test_result }} {{ auto_create_test_result }}
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %}<script>$('.mailbox-select').multipleSelect();</script>{% endblock %}
{% block script %}
<script>
$('.mailbox-select').multipleSelect();
</script>
{% endblock %}

View File

@ -1,12 +1,13 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% if custom_domain.is_sl_subdomain %} {% if custom_domain.is_sl_subdomain %}
{% set active_page = "subdomain" %} {% set active_page = "subdomain" %}
{% else %} {% else %}
{% set active_page = "custom_domain" %} {% set active_page = "custom_domain" %}
{% endif %} {% endif %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col-lg-3 order-lg-1 mb-4"> <div class="col-lg-3 order-lg-1 mb-4">
<div class="list-group list-group-transparent mb-0"> <div class="list-group list-group-transparent mb-0">
@ -14,39 +15,31 @@
class="list-group-item list-group-item-action {{ 'active' if domain_detail_page == 'info' }}"> class="list-group-item list-group-item-action {{ 'active' if domain_detail_page == 'info' }}">
<span class="icon mr-3"><i class="fe fe-flag"></i></span>Info <span class="icon mr-3"><i class="fe fe-flag"></i></span>Info
</a> </a>
{% if not custom_domain.is_sl_subdomain %} {% if not custom_domain.is_sl_subdomain %}
<a href="{{ url_for('dashboard.domain_detail_dns', custom_domain_id=custom_domain.id) }}" <a href="{{ url_for('dashboard.domain_detail_dns', custom_domain_id=custom_domain.id) }}"
class="list-group-item list-group-item-action {{ 'active' if domain_detail_page == 'dns' }}"> class="list-group-item list-group-item-action {{ 'active' if domain_detail_page == 'dns' }}">
<span class="icon mr-3"><i class="fe fe-cloud"></i></span>DNS <span class="icon mr-3"><i class="fe fe-cloud"></i></span>DNS
</a> </a>
{% endif %} {% endif %}
<a href="{{ url_for('dashboard.domain_detail_trash', custom_domain_id=custom_domain.id) }}" <a href="{{ url_for('dashboard.domain_detail_trash', custom_domain_id=custom_domain.id) }}"
class="list-group-item list-group-item-action {{ 'active' if domain_detail_page == 'trash' }}"> class="list-group-item list-group-item-action {{ 'active' if domain_detail_page == 'trash' }}">
<span class="icon mr-3"><i class="fe fe-trash"></i></span>Deleted Alias <span class="icon mr-3"><i class="fe fe-trash"></i></span>Deleted Alias
</a> </a>
<a href="{{ url_for('dashboard.domain_detail_auto_create', custom_domain_id=custom_domain.id) }}" <a href="{{ url_for('dashboard.domain_detail_auto_create', custom_domain_id=custom_domain.id) }}"
class="list-group-item list-group-item-action {{ 'active' if domain_detail_page == 'auto_create' }}"> class="list-group-item list-group-item-action {{ 'active' if domain_detail_page == 'auto_create' }}">
<span class="icon mr-3"><i class="fe fe-layers"></i></span>Auto Create <span class="icon mr-3"><i class="fe fe-layers"></i></span>Auto Create
</a> </a>
</div> </div>
</div> </div>
<div class="col-lg-9"> <div class="col-lg-9">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="text-wrap p-lg-6 domain_detail_content"> <div class="text-wrap p-lg-6 domain_detail_content">
{% block domain_detail_content %} {% block domain_detail_content %}{% endblock %}
{% endblock %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,136 +1,134 @@
{% extends 'dashboard/domain_detail/base.html' %} {% extends "dashboard/domain_detail/base.html" %}
{% set domain_detail_page = "dns" %} {% set domain_detail_page = "dns" %}
{% block title %}{{ custom_domain.domain }} DNS{% endblock %}
{% block title %}
{{ custom_domain.domain }} DNS
{% endblock %}
{% block domain_detail_content %} {% block domain_detail_content %}
<div class="p-4 mr-auto" style="max-width: 60rem;"> <div class="p-4 mr-auto" style="max-width: 60rem;">
<h1 class="h2"> {{ custom_domain.domain }} </h1> <h1 class="h2">{{ custom_domain.domain }}</h1>
<div class="">Please follow the steps below to set up your domain.</div> <div>Please follow the steps below to set up your domain.</div>
<div class="small-text mb-5">DNS changes could take up to 24 hours to update.</div>
<div class="small-text mb-5">
DNS changes could take up to 24 hours to update.
</div>
{% if not custom_domain.ownership_verified %} {% if not custom_domain.ownership_verified %}
<div id="ownership-form">
<div class="font-weight-bold">Domain ownership verification
<div id="ownership-form">
<div class="font-weight-bold">
Domain ownership verification
{% if custom_domain.ownership_verified %} {% if custom_domain.ownership_verified %}
<span class="cursor" data-toggle="tooltip" data-original-title="Domain Ownership Verified"></span>
<span class="cursor"
data-toggle="tooltip"
data-original-title="Domain Ownership Verified">✅</span>
{% else %} {% else %}
<span class="cursor" data-toggle="tooltip" data-original-title="Domain Ownership Required">🚫 </span> <span class="cursor"
data-toggle="tooltip"
data-original-title="Domain Ownership Required">🚫</span>
{% endif %} {% endif %}
</div> </div>
{% if not custom_domain.ownership_verified %} {% if not custom_domain.ownership_verified %}
<div class="mb-2"> <div class="mb-2">
To verify ownership of the domain, please add the following TXT record. To verify ownership of the domain, please add the following TXT record.
Some domain registrars (Namecheap, CloudFlare, etc) might use <em>@</em> for the root domain. Some domain registrars (Namecheap, CloudFlare, etc) might use <em>@</em> for the root domain.
</div> </div>
<div class="mb-3 p-3 dns-record"> <div class="mb-3 p-3 dns-record">
Record: TXT <br> Record: TXT
Domain: {{ custom_domain.domain }} or <b>@</b> <br> <br />
Domain: {{ custom_domain.domain }} or <b>@</b>
<br />
Value: <em data-toggle="tooltip" Value: <em data-toggle="tooltip"
title="Click to copy" title="Click to copy"
class="clipboard" class="clipboard"
data-clipboard-text="{{ custom_domain.get_ownership_dns_txt_value() }}">{{ custom_domain.get_ownership_dns_txt_value() }}</em> data-clipboard-text="{{ custom_domain.get_ownership_dns_txt_value() }}">{{ custom_domain.get_ownership_dns_txt_value() }}</em>
</div> </div>
<form method="post" action="#ownership-form"> <form method="post" action="#ownership-form">
<input type="hidden" name="form-name" value="check-ownership"> <input type="hidden" name="form-name" value="check-ownership">
<button type="submit" class="btn btn-primary"> Verify</button> <button type="submit" class="btn btn-primary">Verify</button>
</form> </form>
{% if not ownership_ok %} {% if not ownership_ok %}
<div class="text-danger mt-4"> <div class="text-danger mt-4">
Your DNS is not correctly set. The TXT record we obtain is: Your DNS is not correctly set. The TXT record we obtain is:
<div class="mb-3 p-3 dns-record"> <div class="mb-3 p-3 dns-record">
{% if not ownership_errors %} {% if not ownership_errors %}(Empty){% endif %}
(Empty)
{% endif %}
{% for r in ownership_errors %} {% for r in ownership_errors %}
{{ r }} <br>
{{ r }}
<br />
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div>
{% endif %}
<hr>
{% endif %}
<div
class="{% if not custom_domain.ownership_verified %} disabled-content {% endif %}"
id="dns-setup">
{% if not custom_domain.ownership_verified %}
<div class="alert alert-warning">
A domain ownership must be verified first.
</div> </div>
{% endif %} {% endif %}
<hr />
{% endif %}
<div class="{% if not custom_domain.ownership_verified %} disabled-content{% endif %}"
id="dns-setup">
{% if not custom_domain.ownership_verified %}
<div class="alert alert-warning">A domain ownership must be verified first.</div>
{% endif %}
<div id="mx-form"> <div id="mx-form">
<div class="font-weight-bold">1. MX record <div class="font-weight-bold">
1. MX record
{% if custom_domain.verified %} {% if custom_domain.verified %}
<span class="cursor" data-toggle="tooltip" data-original-title="MX Record Verified"></span>
<span class="cursor"
data-toggle="tooltip"
data-original-title="MX Record Verified">✅</span>
{% else %} {% else %}
<span class="cursor" data-toggle="tooltip" data-original-title="MX Record Not Verified">🚫 </span> <span class="cursor"
data-toggle="tooltip"
data-original-title="MX Record Not Verified">🚫</span>
{% endif %} {% endif %}
</div> </div>
<div class="mb-2">
<div class="mb-2">Add the following MX DNS record to your domain. <br> Add the following MX DNS record to your domain.
<br />
Please note that there's a dot (<em>.</em>) at the end target addresses. Please note that there's a dot (<em>.</em>) at the end target addresses.
If your domain registrar doesn't allow this trailing dot, please remove it when adding the DNS record. If your domain registrar doesn't allow this trailing dot, please remove it when adding the DNS record.
<br> <br />
Some domain registrars (Namecheap, CloudFlare, etc) might also use <em>@</em> for the root domain. Some domain registrars (Namecheap, CloudFlare, etc) might also use <em>@</em> for the root domain.
</div> </div>
{% for priority, email_server in EMAIL_SERVERS_WITH_PRIORITY %} {% for priority, email_server in EMAIL_SERVERS_WITH_PRIORITY %}
<div class="mb-3 p-3 dns-record"> <div class="mb-3 p-3 dns-record">
Record: MX <br> Record: MX
<br />
Domain: {{ custom_domain.domain }} or Domain: {{ custom_domain.domain }} or
<b>@</b> <br> <b>@</b>
Priority: {{ priority }} <br> <br />
Priority: {{ priority }}
<br />
Target: <em data-toggle="tooltip" Target: <em data-toggle="tooltip"
title="Click to copy" title="Click to copy"
class="clipboard" class="clipboard"
data-clipboard-text="{{ email_server }}">{{ email_server }}</em> data-clipboard-text="{{ email_server }}">{{ email_server }}</em>
</div> </div>
{% endfor %} {% endfor %}
<form method="post" action="#mx-form"> <form method="post" action="#mx-form">
<input type="hidden" name="form-name" value="check-mx"> <input type="hidden" name="form-name" value="check-mx">
{% if custom_domain.verified %} {% if custom_domain.verified %}
<button type="submit" class="btn btn-outline-primary">
Re-verify <button type="submit" class="btn btn-outline-primary">Re-verify</button>
</button>
{% else %} {% else %}
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">Verify</button>
Verify
</button>
{% endif %} {% endif %}
</form> </form>
{% if not mx_ok %} {% if not mx_ok %}
<div class="text-danger mt-4"> <div class="text-danger mt-4">
Your DNS is not correctly set. The MX record we obtain is: Your DNS is not correctly set. The MX record we obtain is:
<div class="mb-3 p-3 dns-record"> <div class="mb-3 p-3 dns-record">
{% if not mx_errors %} {% if not mx_errors %}(Empty){% endif %}
(Empty)
{% endif %}
{% for r in mx_errors %} {% for r in mx_errors %}
{{ r }} <br>
{{ r }}
<br />
{% endfor %} {% endfor %}
</div> </div>
{% if custom_domain.verified %} {% if custom_domain.verified %}
<div class="alert alert-danger"> <div class="alert alert-danger">
Without the MX record set up correctly, you can miss emails sent to your aliases. Without the MX record set up correctly, you can miss emails sent to your aliases.
Please update the MX record ASAP. Please update the MX record ASAP.
@ -139,33 +137,40 @@
</div> </div>
{% endif %} {% endif %}
</div> </div>
<hr />
<hr>
<div id="spf-form"> <div id="spf-form">
<div class="font-weight-bold">2. SPF (Optional) <div class="font-weight-bold">
2. SPF (Optional)
{% if custom_domain.spf_verified %} {% if custom_domain.spf_verified %}
<span class="cursor" data-toggle="tooltip" data-original-title="SPF Verified"></span>
<span class="cursor"
data-toggle="tooltip"
data-original-title="SPF Verified">✅</span>
{% else %} {% else %}
<span class="cursor" data-toggle="tooltip" data-original-title="SPF Not Verified">🚫 </span> <span class="cursor"
data-toggle="tooltip"
data-original-title="SPF Not Verified">🚫</span>
{% endif %} {% endif %}
</div> </div>
<div> <div>
SPF <a href="https://en.wikipedia.org/wiki/Sender_Policy_Framework" target="_blank" SPF
rel="noopener">(Wikipedia↗)</a> is an email <a href="https://en.wikipedia.org/wiki/Sender_Policy_Framework"
target="_blank"
rel="noopener">(Wikipedia↗)</a>
is an email
authentication method authentication method
designed to detect forging sender addresses during the delivery of the email. <br> designed to detect forging sender addresses during the delivery of the email.
<br />
Setting up SPF is highly recommended to reduce the chance your emails ending up in the recipient's Spam Setting up SPF is highly recommended to reduce the chance your emails ending up in the recipient's Spam
folder. folder.
</div> </div>
<div class="mb-2">Add the following TXT DNS record to your domain.</div> <div class="mb-2">Add the following TXT DNS record to your domain.</div>
<div class="mb-2 p-3 dns-record"> <div class="mb-2 p-3 dns-record">
Record: TXT <br> Record: TXT
<br />
Domain: {{ custom_domain.domain }} or Domain: {{ custom_domain.domain }} or
<b>@</b> <br> <b>@</b>
<br />
Value: Value:
<em data-toggle="tooltip" <em data-toggle="tooltip"
title="Click to copy" title="Click to copy"
@ -174,92 +179,100 @@
{{ spf_record }} {{ spf_record }}
</em> </em>
</div> </div>
<form method="post" action="#spf-form"> <form method="post" action="#spf-form">
<input type="hidden" name="form-name" value="check-spf"> <input type="hidden" name="form-name" value="check-spf">
{% if custom_domain.spf_verified %} {% if custom_domain.spf_verified %}
<button type="submit" class="btn btn-outline-primary">
Re-verify <button type="submit" class="btn btn-outline-primary">Re-verify</button>
</button>
{% else %} {% else %}
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">Verify</button>
Verify
</button>
{% endif %} {% endif %}
</form> </form>
{% if not spf_ok %} {% if not spf_ok %}
<div class="text-danger mt-4"> <div class="text-danger mt-4">
Your DNS is not correctly set. The TXT record we obtain is: Your DNS is not correctly set. The TXT record we obtain is:
<div class="mb-3 p-3 dns-record"> <div class="mb-3 p-3 dns-record">
{% if not spf_errors %} {% if not spf_errors %}(Empty){% endif %}
(Empty)
{% endif %}
{% for r in spf_errors %} {% for r in spf_errors %}
{{ r }} <br>
{{ r }}
<br />
{% endfor %} {% endfor %}
</div> </div>
{% if custom_domain.spf_verified %} {% if custom_domain.spf_verified %}
Without SPF setup, emails you sent from your alias might end up in Spam/Junk folder. Without SPF setup, emails you sent from your alias might end up in Spam/Junk folder.
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
</div> </div>
<hr />
<hr>
<div id="dkim-form"> <div id="dkim-form">
<div class="font-weight-bold">3. DKIM (Optional) <div class="font-weight-bold">
3. DKIM (Optional)
{% if custom_domain.dkim_verified %} {% if custom_domain.dkim_verified %}
<span class="cursor" data-toggle="tooltip" data-original-title="SPF Verified"></span>
<span class="cursor"
data-toggle="tooltip"
data-original-title="SPF Verified">✅</span>
{% else %} {% else %}
<span class="cursor" data-toggle="tooltip" data-original-title="DKIM Not Verified">🚫 </span> <span class="cursor"
data-toggle="tooltip"
data-original-title="DKIM Not Verified">🚫</span>
{% endif %} {% endif %}
</div> </div>
<div> <div>
DKIM <a href="https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail" target="_blank" rel="noopener">(Wikipedia↗)</a> DKIM
<a href="https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail"
target="_blank"
rel="noopener">(Wikipedia↗)</a>
is an is an
email email
authentication method authentication method
designed to avoid email spoofing. <br> designed to avoid email spoofing.
<br />
Setting up DKIM is highly recommended to reduce the chance your emails ending up in the recipient's Spam Setting up DKIM is highly recommended to reduce the chance your emails ending up in the recipient's Spam
folder. folder.
</div> </div>
<div class="mb-2">
<div class="mb-2">Add the following CNAME DNS record to your domain.</div> Add the following CNAME DNS record to your domain.
</div>
<div class="mb-2 p-3 dns-record"> <div class="mb-2 p-3 dns-record">
Record: CNAME <br> Record: CNAME
<br />
Domain: <em data-toggle="tooltip" Domain: <em data-toggle="tooltip"
title="Click to copy" title="Click to copy"
class="clipboard" class="clipboard"
data-clipboard-text="dkim._domainkey">dkim._domainkey</em> <br> data-clipboard-text="dkim._domainkey">dkim._domainkey</em>
<br />
Value: Value:
<em data-toggle="tooltip" <em data-toggle="tooltip"
title="Click to copy" title="Click to copy"
class="clipboard" class="clipboard"
data-clipboard-text="{{ dkim_cname + '.' }}" style="overflow-wrap: break-word"> data-clipboard-text="{{ dkim_cname + '.' }}"
style="overflow-wrap: break-word">
{{ dkim_cname }}. {{ dkim_cname }}.
</em> </em>
</div> </div>
<div class="alert alert-info"> <div class="alert alert-info">
Some DNS registrar might require a full record path, in this case please use Some DNS registrar might require a full record path, in this case please use
<i>dkim._domainkey.{{ custom_domain.domain }}</i> as domain value instead. <br> <i>dkim._domainkey.{{ custom_domain.domain }}</i> as domain value instead.
<br />
If you are using a subdomain, e.g. <i>subdomain.domain.com</i>, If you are using a subdomain, e.g. <i>subdomain.domain.com</i>,
you need to use <i>dkim._domainkey.subdomain</i> as domain value instead. you need to use <i>dkim._domainkey.subdomain</i> as domain value instead.
<br> <br />
</div> </div>
<div class="alert alert-info"> <div class="alert alert-info">
If you are using CloudFlare, please make sure to <b>not</b> select the Proxy option. <br><br> If you are using CloudFlare, please make sure to <b>not</b> select the Proxy option.
<br />
<br />
<img src="/static/images/cloudflare-proxy.png" class="w-100"> <img src="/static/images/cloudflare-proxy.png" class="w-100">
</div> </div>
<form method="post" action="#dkim-form"> <form method="post" action="#dkim-form">
<input type="hidden" name="form-name" value="check-dkim"> <input type="hidden" name="form-name" value="check-dkim">
{% if custom_domain.dkim_verified %} {% if custom_domain.dkim_verified %}
<button type="submit" class="btn btn-outline-primary"> <button type="submit" class="btn btn-outline-primary">
Re-verify Re-verify
</button> </button>
@ -269,54 +282,67 @@
</button> </button>
{% endif %} {% endif %}
</form> </form>
{% if not dkim_ok %} {% if not dkim_ok %}
<div class="text-danger mt-4"> <div class="text-danger mt-4">
Your DNS is not correctly set. Your DNS is not correctly set.
{% if dkim_errors %} {% if dkim_errors %}
The CNAME record we obtain for The CNAME record we obtain for
<em>dkim._domainkey.{{ custom_domain.domain }}</em> is: <em>dkim._domainkey.{{ custom_domain.domain }}</em> is:
<div class="mb-3 p-3 dns-record"> <div class="mb-3 p-3 dns-record">
{% for r in dkim_errors %} {% for r in dkim_errors %}
{{ r }} <br>
{{ r }}
<br />
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
{% if custom_domain.dkim_verified %} {% if custom_domain.dkim_verified %}
Without DKIM setup, emails you sent from your alias might end up in Spam/Junk folder. Without DKIM setup, emails you sent from your alias might end up in Spam/Junk folder.
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
</div> </div>
<hr />
<hr>
<div id="dmarc-form"> <div id="dmarc-form">
<div class="font-weight-bold">4. DMARC (Optional) <div class="font-weight-bold">
4. DMARC (Optional)
{% if custom_domain.dmarc_verified %} {% if custom_domain.dmarc_verified %}
<span class="cursor" data-toggle="tooltip" data-original-title="DMARC Verified"></span>
<span class="cursor"
data-toggle="tooltip"
data-original-title="DMARC Verified">✅</span>
{% else %} {% else %}
<span class="cursor" data-toggle="tooltip" data-original-title="DMARC Not Verified">🚫 </span> <span class="cursor"
data-toggle="tooltip"
data-original-title="DMARC Not Verified">🚫 </span>
{% endif %} {% endif %}
</div> </div>
<div> <div>
DMARC <a href="https://en.wikipedia.org/wiki/DMARC" target="_blank" rel="noopener">(Wikipedia↗)</a> DMARC
is designed to protect the domain from unauthorized use, commonly known as email spoofing. <br> <a href="https://en.wikipedia.org/wiki/DMARC"
target="_blank"
rel="noopener">
(Wikipedia↗)
</a>
is designed to protect the domain from unauthorized use, commonly known as email spoofing.
<br />
Built around SPF and DKIM, a DMARC policy tells the receiving mail server what to do if Built around SPF and DKIM, a DMARC policy tells the receiving mail server what to do if
neither of those authentication methods passes. neither of those authentication methods passes.
</div> </div>
<div class="mb-2">
<div class="mb-2">Add the following TXT DNS record to your domain.</div> Add the following TXT DNS record to your domain.
</div>
<div class="mb-2 p-3 dns-record"> <div class="mb-2 p-3 dns-record">
Record: TXT <br> Record: TXT
<br />
Domain: <em data-toggle="tooltip" Domain: <em data-toggle="tooltip"
title="Click to copy" title="Click to copy"
class="clipboard" class="clipboard"
data-clipboard-text="_dmarc">_dmarc</em> <br> data-clipboard-text="_dmarc">_dmarc</em>
<br />
Value: Value:
<em data-toggle="tooltip" <em data-toggle="tooltip"
title="Click to copy" title="Click to copy"
@ -325,18 +351,18 @@
{{ dmarc_record }} {{ dmarc_record }}
</em> </em>
</div> </div>
<div class="alert alert-info"> <div class="alert alert-info">
Some DNS registrar might require a full record path, in this case please use Some DNS registrar might require a full record path, in this case please use
<i>_dmarc.{{ custom_domain.domain }}</i> as domain value instead. <br> <i>_dmarc.{{ custom_domain.domain }}</i> as domain value instead.
<br />
If you are using a subdomain, e.g. <i>subdomain.domain.com</i>, If you are using a subdomain, e.g. <i>subdomain.domain.com</i>,
you need to use <i>_dmarc.subdomain</i> as domain value instead. you need to use <i>_dmarc.subdomain</i> as domain value instead.
<br> <br />
</div> </div>
<form method="post" action="#dmarc-form"> <form method="post" action="#dmarc-form">
<input type="hidden" name="form-name" value="check-dmarc"> <input type="hidden" name="form-name" value="check-dmarc">
{% if custom_domain.dmarc_verified %} {% if custom_domain.dmarc_verified %}
<button type="submit" class="btn btn-outline-primary"> <button type="submit" class="btn btn-outline-primary">
Re-verify Re-verify
</button> </button>
@ -346,27 +372,26 @@
</button> </button>
{% endif %} {% endif %}
</form> </form>
{% if not dmarc_ok %} {% if not dmarc_ok %}
<div class="text-danger mt-4"> <div class="text-danger mt-4">
Your DNS is not correctly set. Your DNS is not correctly set.
The TXT record we obtain is: The TXT record we obtain is:
<div class="mb-3 p-3" style="background-color: #eee"> <div class="mb-3 p-3" style="background-color: #eee">
{% if not dmarc_errors %} {% if not dmarc_errors %}(Empty){% endif %}
(Empty)
{% endif %}
{% for r in dmarc_errors %} {% for r in dmarc_errors %}
{{ r }} <br>
{{ r }}
<br />
{% endfor %} {% endfor %}
</div> </div>
{% if custom_domain.dmarc_verified %} {% if custom_domain.dmarc_verified %}
Without DMARC setup, emails sent from your alias might end up in the Spam/Junk folder. Without DMARC setup, emails sent from your alias might end up in the Spam/Junk folder.
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,71 +1,59 @@
{% extends 'dashboard/domain_detail/base.html' %} {% extends "dashboard/domain_detail/base.html" %}
{% set domain_detail_page = "info" %} {% set domain_detail_page = "info" %}
{% block title %}{{ custom_domain.domain }} Info{% endblock %}
{% block title %}
{{ custom_domain.domain }} Info
{% endblock %}
{% block domain_detail_content %} {% block domain_detail_content %}
<h1 class="h2 mb-1"> {{ custom_domain.domain }} </h1>
<h1 class="h2 mb-1">{{ custom_domain.domain }}</h1>
<div class="small-text">Created {{ custom_domain.created_at | dt }}. {{ nb_alias }} aliases</div> <div class="small-text">Created {{ custom_domain.created_at | dt }}. {{ nb_alias }} aliases</div>
<hr />
<hr> <h3 class="mb-1">Auto create/on the fly alias</h3>
<h3 class="mb-1">Auto create/on the fly alias </h3>
<div> <div>
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="switch-catch-all"> <input type="hidden" name="form-name" value="switch-catch-all">
<label class="custom-switch cursor mt-2 pl-0" data-toggle="tooltip" {% if custom_domain.catch_all %}
<label class="custom-switch cursor mt-2 pl-0" title="Disable catch-all" {% else %} title="Enable catch-all" {% endif %}>
data-toggle="tooltip" <input type="checkbox" class="custom-switch-input" {{ "checked" if custom_domain.catch_all else "" }}>
{% if custom_domain.catch_all %}
title="Disable catch-all"
{% else %}
title="Enable catch-all"
{% endif %}
>
<input type="checkbox" class="custom-switch-input"
{{ "checked" if custom_domain.catch_all else "" }}>
<span class="custom-switch-indicator"></span> <span class="custom-switch-indicator"></span>
<spam class="ml-2"> <spam class="ml-2">
Catch All Catch All
</spam> </spam>
</label> </label>
</form> </form>
<div class=""> <div>
Simply use <b>anything@{{ custom_domain.domain }}</b> Simply use <b>anything@{{ custom_domain.domain }}</b>
next time you need an alias: it'll be <b>automatically</b> next time you need an alias: it'll be <b>automatically</b>
created the first time it receives an email. created the first time it receives an email.
To have more fine-grained control, you can also define To have more fine-grained control, you can also define
<a href="{{ url_for('dashboard.domain_detail_auto_create', custom_domain_id=custom_domain.id) }}">auto create <a href="{{ url_for('dashboard.domain_detail_auto_create', custom_domain_id=custom_domain.id) }}">
auto create
rules rules
<i class="fe fe-chevrons-right"></i></a>. <i class="fe fe-chevrons-right"></i>
</a>
.
</div> </div>
</div> </div>
<div class="{% if not custom_domain.catch_all %} disabled-content{% endif %}">
<div class="{% if not custom_domain.catch_all %} disabled-content {% endif %}"> <div>
<div>Auto-created aliases are automatically owned by the following mailboxes Auto-created aliases are automatically owned by the following mailboxes
<i class="fe fe-corner-right-down"></i></a>. <i class="fe fe-corner-right-down"></i>
.
</div> </div>
{% set domain_mailboxes=custom_domain.mailboxes %} {% set domain_mailboxes=custom_domain.mailboxes %}
<form method="post" class="mt-2"> <form method="post" class="mt-2">
<input type="hidden" name="form-name" value="update"> <input type="hidden" name="form-name" value="update">
<input type="hidden" name="domain-id" value="{{ custom_domain.id }}"> <input type="hidden" name="domain-id" value="{{ custom_domain.id }}">
<div class="d-flex"> <div class="d-flex">
<div class="flex-grow-1 mr-2"> <div class="flex-grow-1 mr-2">
<select data-width="100%" required <select data-width="100%"
class="mailbox-select" multiple name="mailbox_ids"> required
class="mailbox-select"
multiple
name="mailbox_ids">
{% for mailbox in mailboxes %} {% for mailbox in mailboxes %}
<option value="{{ mailbox.id }}" {% if mailbox in domain_mailboxes %}
selected {% endif %}> <option value="{{ mailbox.id }}"
{% if mailbox in domain_mailboxes %} selected{% endif %}>
{{ mailbox.email }} {{ mailbox.email }}
</option> </option>
{% endfor %} {% endfor %}
@ -77,15 +65,12 @@
</div> </div>
</form> </form>
</div> </div>
<hr />
<hr>
<h3 class="mb-1">Default Display Name</h3> <h3 class="mb-1">Default Display Name</h3>
<div class=""> <div>
Default display name for aliases created with <b>{{ custom_domain.domain }}</b> Default display name for aliases created with <b>{{ custom_domain.domain }}</b>
unless overwritten by the alias display name. unless overwritten by the alias display name.
</div> </div>
<div> <div>
<form method="post" class="form-inline"> <form method="post" class="form-inline">
<input type="hidden" name="form-name" value="set-name"> <input type="hidden" name="form-name" value="set-name">
@ -97,47 +82,40 @@
</div> </div>
<button class="btn btn-outline-primary" name="action" value="save">Save</button> <button class="btn btn-outline-primary" name="action" value="save">Save</button>
{% if custom_domain.name %} {% if custom_domain.name %}
<button class="btn btn-outline-danger float-right ml-2" name="action" value="remove">Remove</button>
<button class="btn btn-outline-danger float-right ml-2"
name="action"
value="remove">Remove</button>
{% endif %} {% endif %}
</form> </form>
</div> </div>
<hr />
<hr>
<h3 class="mb-1">Random Prefix Generation</h3> <h3 class="mb-1">Random Prefix Generation</h3>
<div class=""> <div>Add a random prefix for this domain when creating a new alias.</div>
Add a random prefix for this domain when creating a new alias.
</div>
<div> <div>
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="switch-random-prefix-generation"> <input type="hidden"
<label class="custom-switch cursor mt-2 pl-0" name="form-name"
data-toggle="tooltip" value="switch-random-prefix-generation">
{% if custom_domain.random_prefix_generation %} <label class="custom-switch cursor mt-2 pl-0" data-toggle="tooltip" {% if custom_domain.random_prefix_generation %}
title="Disable random prefix generation" title="Disable random prefix generation" {% else %} title="Enable random prefix generation" {% endif %}>
{% else %} <input type="checkbox" class="custom-switch-input" {{ "checked" if custom_domain.random_prefix_generation else "" }}>
title="Enable random prefix generation"
{% endif %}
>
<input type="checkbox" class="custom-switch-input"
{{ "checked" if custom_domain.random_prefix_generation else "" }}>
<span class="custom-switch-indicator"></span> <span class="custom-switch-indicator"></span>
</label> </label>
</form> </form>
</div> </div>
<hr />
<hr>
<h3 class="mb-1"> <h3 class="mb-1">
{% if custom_domain.is_sl_subdomain %} {% if custom_domain.is_sl_subdomain %}
Delete Subdomain Delete Subdomain
{% else %} {% else %}
Delete Domain Delete Domain
{% endif %} {% endif %}
</h3> </h3>
<div class="mb-3"> <div class="mb-3">
{% if custom_domain.is_sl_subdomain %} {% if custom_domain.is_sl_subdomain %}
<div class="alert alert-danger"> <div class="alert alert-danger">
This operation is <b>irreversible</b>. This operation is <b>irreversible</b>.
All aliases associated with this subdomain will be deleted. All aliases associated with this subdomain will be deleted.
@ -155,20 +133,13 @@
</div> </div>
{% endif %} {% endif %}
</div> </div>
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="delete"> <input type="hidden" name="form-name" value="delete">
<span <span class="delete-custom-domain btn btn-danger">Delete {{ custom_domain.domain }}</span>
class="delete-custom-domain btn btn-danger">
Delete {{ custom_domain.domain }}
</span>
</form> </form>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
$('.mailbox-select').multipleSelect(); $('.mailbox-select').multipleSelect();
@ -180,7 +151,7 @@
let that = $(this); let that = $(this);
bootbox.confirm({ bootbox.confirm({
message: "All aliases associated with <b>{{ custom_domain.domain }}</b> will be also deleted. <br>" + message: "All aliases associated with <b>{{ custom_domain.domain }}</b> will be also deleted. <br />" +
"This operation is not reversible, please confirm.", "This operation is not reversible, please confirm.",
buttons: { buttons: {
confirm: { confirm: {
@ -201,4 +172,3 @@
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,27 +1,30 @@
{% extends 'dashboard/domain_detail/base.html' %} {% extends "dashboard/domain_detail/base.html" %}
{% set domain_detail_page = "trash" %} {% set domain_detail_page = "trash" %}
{% block title %}{{ custom_domain.domain }} deleted aliases{% endblock %}
{% block title %}
{{ custom_domain.domain }} deleted aliases
{% endblock %}
{% block domain_detail_content %} {% block domain_detail_content %}
<h1 class="h3"> {{ custom_domain.domain }} deleted alias (aka Trash)
<a class="ml-3 text-info" style="font-size: 12px" data-toggle="collapse" href="#howtouse" role="button" <h1 class="h3">
aria-expanded="false" aria-controls="collapseExample"> {{ custom_domain.domain }} deleted alias (aka Trash)
<a class="ml-3 text-info"
style="font-size: 12px"
data-toggle="collapse"
href="#howtouse"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
How to use <i class="fe fe-chevrons-down"></i> How to use <i class="fe fe-chevrons-down"></i>
</a> </a>
</h1> </h1>
<div class="alert alert-primary collapse" id="howtouse" role="alert"> <div class="alert alert-primary collapse" id="howtouse" role="alert">
On this page you can view all aliases that have been deleted and belong to the domain On this page you can view all aliases that have been deleted and belong to the domain
<b>{{ custom_domain.domain }}</b>. <br> <b>{{ custom_domain.domain }}</b>.
<br />
When an alias is in the trash, it cannot be re-created, either via the alias creation page or on-the-fly with the When an alias is in the trash, it cannot be re-created, either via the alias creation page or on-the-fly with the
domain catch-all option. domain catch-all option.
</div> </div>
{% if domain_deleted_aliases | length > 0 %} {% if domain_deleted_aliases | length > 0 %}
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="empty-all"> <input type="hidden" name="form-name" value="empty-all">
<button class="btn btn-outline-danger">Empty Trash</button> <button class="btn btn-outline-danger">Empty Trash</button>
@ -33,10 +36,9 @@
{% else %} {% else %}
There's no deleted alias recorded for this domain. There's no deleted alias recorded for this domain.
{% endif %} {% endif %}
{% for deleted_alias in domain_deleted_aliases %} {% for deleted_alias in domain_deleted_aliases %}
<hr>
<hr />
<b>{{ deleted_alias.email }}</b> - <b>{{ deleted_alias.email }}</b> -
deleted {{ deleted_alias.created_at | dt }} deleted {{ deleted_alias.created_at | dt }}
<form method="post"> <form method="post">
@ -45,6 +47,4 @@
<button class="btn btn-sm btn-outline-warning">Remove from trash</button> <button class="btn btn-sm btn-outline-warning">Remove from trash</button>
</form> </form>
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}

View File

@ -1,11 +1,9 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "setting" %} {% set active_page = "setting" %}
{% block title %} {% block title %}SUDO MODE{% endblock %}
SUDO MODE
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h2">Entering Sudo Mode</h1> <h1 class="h2">Entering Sudo Mode</h1>
@ -15,17 +13,13 @@
<p> <p>
Please enter your account password so that we can ensure it's you. Please enter your account password so that we can ensure it's you.
</p> </p>
<form method="post"> <form method="post">
{{ password_check_form.csrf_token }} {{ password_check_form.csrf_token }}
<div class="font-weight-bold mt-5">Password</div> <div class="font-weight-bold mt-5">Password</div>
{{ password_check_form.password(class="form-control", autofocus="true") }} {{ password_check_form.password(class="form-control", autofocus="true") }}
{{ render_field_errors(password_check_form.password) }} {{ render_field_errors(password_check_form.password) }}
<button class="btn btn-lg btn-danger mt-2">Submit</button> <button class="btn btn-lg btn-danger mt-2">Submit</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,36 +1,26 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}Extend Subscription{% endblock %}
{% block title %}
Extend Subscription
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h2">Extend Subscription</h1> <h1 class="h2">Extend Subscription</h1>
<p> <p>
Your subscription is expired on {{ coinbase_subscription.end_at.format("YYYY-MM-DD") }} Your subscription is expired on {{ coinbase_subscription.end_at.format("YYYY-MM-DD") }}
</p> </p>
<div> <div>
<a class="buy-with-crypto" data-custom="{{ current_user.id }}" <a class="buy-with-crypto"
href="{{ coinbase_url }}"> data-custom="{{ current_user.id }}"
Extend for 1 year - $30 href="{{ coinbase_url }}">Extend for 1 year - $30</a>
</a> <script src="https://commerce.coinbase.com/v1/checkout.js?version=201807"></script>
<script src="https://commerce.coinbase.com/v1/checkout.js?version=201807">
</script>
</div> </div>
<div class="mt-2"> <div class="mt-2">
Your subscription will be extended when the payment is confirmed and we'll send you a confirmation email. <br> Your subscription will be extended when the payment is confirmed and we'll send you a confirmation email.
<br />
Please note that it can take up to 1h for processing a cryptocurrency payment. Please note that it can take up to 1h for processing a cryptocurrency payment.
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,24 +1,23 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "setting" %}
{% block title %}
Manage Security Key
{% endblock %}
{% set active_page = "setting" %}
{% block title %}Manage Security Key{% endblock %}
{% block head %} {% block head %}
<script src="{{ url_for('static', filename='node_modules/qrious/dist/qrious.min.js') }}"></script> <script src="{{ url_for('static', filename='node_modules/qrious/dist/qrious.min.js') }}"></script>
{% endblock %} {% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h2">Manage Your Security Key</h1> <h1 class="h2">Manage Your Security Key</h1>
<p>Unlink all keys will also disable WebAuthn 2FA.</p> <p>
Unlink all keys will also disable WebAuthn 2FA.
</p>
<form id="formManageKey" method="post"> <form id="formManageKey" method="post">
{{ fido_manage_form.csrf_token }} {{ fido_manage_form.csrf_token }}
{{ fido_manage_form.credential_id(class="form-control", placeholder="") }} {{ fido_manage_form.credential_id(class="form-control", placeholder="") }}
</form> </form>
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@ -29,25 +28,32 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{%for key in keys%} {% for key in keys %}
<tr>
<th scope="row">{{ key.id }}</th> <tr>
<td>{{ key.name }}</td> <th scope="row">{{ key.id }}</th>
<td>{{key.created_at | dt}}</td> <td>{{ key.name }}</td>
<td class="text-center"><button class="btn btn-outline-danger" onclick="$('#credential_id').val('{{ key.credential_id }}'); $('#formManageKey').submit();">Unlink</button></td> <td>{{ key.created_at | dt }}</td>
</tr> <td class="text-center">
{%endfor%} <button class="btn btn-outline-danger"
onclick="$('#credential_id').val('{{ key.credential_id }}'); $('#formManageKey').submit();">
Unlink
</button>
</td>
</tr>
{% endfor %}
<tr> <tr>
<th scope="row">#</th> <th scope="row">#</th>
<td>Link a New Key</td> <td>Link a New Key</td>
<td></td> <td></td>
<td class="text-center"><a href="{{ url_for('dashboard.fido_setup') }}"><button class="btn btn-outline-success">Link</button></a></td> <td class="text-center">
<a href="{{ url_for('dashboard.fido_setup') }}">
<button class="btn btn-outline-success">Link</button>
</a>
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,33 +1,32 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "setting" %}
{% block title %}
Security Key Setup
{% endblock %}
{% set active_page = "setting" %}
{% block title %}Security Key Setup{% endblock %}
{% block head %} {% block head %}
<script src="{{ url_for('static', filename='node_modules/qrious/dist/qrious.min.js') }}"></script> <script src="{{ url_for('static', filename='node_modules/qrious/dist/qrious.min.js') }}"></script>
<script src="{{ url_for('static', filename='assets/js/vendors/base64.js') }}"></script> <script src="{{ url_for('static', filename='assets/js/vendors/base64.js') }}"></script>
<script src="/static/assets/js/vendors/webauthn.js?v={{ VERSION }}"></script> <script src="/static/assets/js/vendors/webauthn.js?v={{ VERSION }}"></script>
{% endblock %} {% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h2 text-center">Register Your Security Key</h1> <h1 class="h2 text-center">Register Your Security Key</h1>
<p class="text-center">Follow your browser's steps to register your security key with SimpleLogin</p> <p class="text-center">
Follow your browser's steps to register your security key with SimpleLogin
</p>
<form id="formRegisterKey" method="post"> <form id="formRegisterKey" method="post">
{{ fido_token_form.csrf_token }} {{ fido_token_form.csrf_token }}
{{ fido_token_form.sk_assertion(class="form-control", placeholder="") }} {{ fido_token_form.sk_assertion(class="form-control", placeholder="") }}
{{ fido_token_form.key_name(id="key-name", class="form-control", placeholder="Name of your key (Required)") }} {{ fido_token_form.key_name(id="key-name", class="form-control", placeholder="Name of your key (Required)") }}
{{ render_field_errors(fido_token_form.key_name) }} {{ render_field_errors(fido_token_form.key_name) }}
<div class="text-center"> <div class="text-center">
<span id="btnRegisterKey" class="btn btn-lg btn-primary mt-2" onclick="registerKey();">Register Key</span> <span id="btnRegisterKey"
class="btn btn-lg btn-primary mt-2"
onclick="registerKey();">Register Key</span>
</div> </div>
</form> </form>
<script> <script>
// disable submit when enter // disable submit when enter
$('form input').keydown(function (e) { $('form input').keydown(function (e) {
@ -71,7 +70,6 @@
} }
</script> </script>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,22 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}Lifetime Licence{% endblock %}
{% block title %}
Lifetime Licence
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h2">Lifetime Licence</h1> <h1 class="h2">Lifetime Licence</h1>
<div class="mb-4"> <div class="mb-4">
If you have a lifetime licence, please paste it here. <br> If you have a lifetime licence, please paste it here.
<br />
</div> </div>
<form method="post"> <form method="post">
{{ coupon_form.csrf_token }} {{ coupon_form.csrf_token }}
{{ coupon_form.code(class="form-control", placeholder="Licence Code") }} {{ coupon_form.code(class="form-control", placeholder="Licence Code") }}
{{ render_field_errors(coupon_form.code) }} {{ render_field_errors(coupon_form.code) }}
<button class="btn btn-success mt-2">Apply</button> <button class="btn btn-success mt-2">Apply</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %}
{% endblock %}

View File

@ -1,131 +1,130 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "mailbox" %} {% set active_page = "mailbox" %}
{% block title %}Mailboxes{% endblock %}
{% block title %}
Mailboxes
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h1 class="h3"> Mailboxes <h1 class="h3">
<a class="ml-3 text-info" style="font-size: 12px" data-toggle="collapse" href="#howtouse" role="button" Mailboxes
aria-expanded="false" aria-controls="collapseExample"> <a class="ml-3 text-info"
style="font-size: 12px"
data-toggle="collapse"
href="#howtouse"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
How to use <i class="fe fe-chevrons-down"></i> How to use <i class="fe fe-chevrons-down"></i>
</a> </a>
</h1> </h1>
{% if not current_user.is_premium() %} {% if not current_user.is_premium() %}
<div class="alert alert-danger" role="alert">
This feature is only available in premium plan. <div class="alert alert-danger" role="alert">This feature is only available in premium plan.</div>
</div>
{% endif %} {% endif %}
<div class="alert alert-primary collapse {% if mailboxes|length == 1 %} show{% endif %}"
<div class="alert alert-primary collapse {% if mailboxes|length == 1 %} show {% endif %}" id="howtouse" role="alert"> id="howtouse"
role="alert">
A <em>mailbox</em> is just another personal email address. When creating a new alias, you could choose the A <em>mailbox</em> is just another personal email address. When creating a new alias, you could choose the
mailbox that <em>owns</em> this alias, i.e: <br> mailbox that <em>owns</em> this alias, i.e:
- all emails sent to this alias will be forwarded to this mailbox <br> <br />
- from this mailbox, you can reply/send emails from the alias. <br><br> - all emails sent to this alias will be forwarded to this mailbox
<br />
- from this mailbox, you can reply/send emails from the alias.
<br />
<br />
When you signed up, a mailbox is automatically created with your email <b>{{ current_user.email }}</b> When you signed up, a mailbox is automatically created with your email <b>{{ current_user.email }}</b>
<br><br> <br />
<br />
The mailbox doesn't have to be your email: it can be your friend's email The mailbox doesn't have to be your email: it can be your friend's email
if you want to create aliases for your buddy. if you want to create aliases for your buddy.
</div> </div>
<div class="row"> <div class="row">
{% for mailbox in mailboxes %} {% for mailbox in mailboxes %}
<div class="col-12 col-lg-6"> <div class="col-12 col-lg-6">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h5 class="card-title"> <h5 class="card-title">
{{ mailbox.email }} {{ mailbox.email }}
{% if mailbox.verified %} {% if mailbox.verified %}
<span class="cursor" data-toggle="tooltip" data-original-title="Mailbox Verified"></span>
<span class="cursor"
data-toggle="tooltip"
data-original-title="Mailbox Verified">✅</span>
{% else %} {% else %}
<span class="cursor" data-toggle="tooltip" data-original-title="Mailbox Not Verified"> <span class="cursor"
🚫 data-toggle="tooltip"
</span> data-original-title="Mailbox Not Verified">🚫</span>
{% endif %} {% endif %}
{% if mailbox.pgp_enabled() %} {% if mailbox.pgp_enabled() %}
<span class="cursor" data-toggle="tooltip" data-original-title="PGP Enabled">🗝</span>
{% endif %}
<span class="cursor"
data-toggle="tooltip"
data-original-title="PGP Enabled">🗝</span>
{% endif %}
{% if mailbox.id == current_user.default_mailbox_id %} {% if mailbox.id == current_user.default_mailbox_id %}
<div class="badge badge-primary float-right" data-toggle="tooltip"
title="When a new random alias is created, it belongs to the default mailbox">Default Mailbox <div class="badge badge-primary float-right"
data-toggle="tooltip"
title="When a new random alias is created, it belongs to the default mailbox">
Default Mailbox
</div> </div>
{% endif %} {% endif %}
</h5> </h5>
<h6 class="card-subtitle mb-2 text-muted"> <h6 class="card-subtitle mb-2 text-muted">
Created {{ mailbox.created_at | dt }} <br> Created {{ mailbox.created_at | dt }}
<span class="font-weight-bold">{{ mailbox.nb_alias() }}</span> aliases. <br> <br />
<span class="font-weight-bold">{{ mailbox.nb_alias() }}</span> aliases.
<br />
</h6> </h6>
<a href="{{ url_for('dashboard.mailbox_detail_route', mailbox_id=mailbox.id) }}">Edit ➡</a> <a href="{{ url_for('dashboard.mailbox_detail_route', mailbox_id=mailbox.id) }}">Edit ➡</a>
</div> </div>
<div class="card-footer p-0"> <div class="card-footer p-0">
<div class="row"> <div class="row">
{% if mailbox.verified %} {% if mailbox.verified %}
<div class="col"> <div class="col">
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="set-default"> <input type="hidden" name="form-name" value="set-default">
<input type="hidden" class="mailbox" value="{{ mailbox.email }}"> <input type="hidden" class="mailbox" value="{{ mailbox.email }}">
<input type="hidden" name="mailbox-id" value="{{ mailbox.id }}"> <input type="hidden" name="mailbox-id" value="{{ mailbox.id }}">
<button class="card-link btn btn-link <button class="card-link btn btn-link {% if mailbox.id == current_user.default_mailbox_id %} disabled{% endif %}">
{% if mailbox.id == current_user.default_mailbox_id %} disabled {% endif %}"
>
Set As Default Mailbox Set As Default Mailbox
</button> </button>
</form> </form>
</div> </div>
{% endif %} {% endif %}
<div class="col"> <div class="col">
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="delete"> <input type="hidden" name="form-name" value="delete">
<input type="hidden" class="mailbox" value="{{ mailbox.email }}"> <input type="hidden" class="mailbox" value="{{ mailbox.email }}">
<input type="hidden" name="mailbox-id" value="{{ mailbox.id }}"> <input type="hidden" name="mailbox-id" value="{{ mailbox.id }}">
<span class="card-link btn btn-link text-danger float-right delete-mailbox <span class="card-link btn btn-link text-danger float-right delete-mailbox {% if mailbox.id == current_user.default_mailbox_id %} disabled{% endif %}">
{% if mailbox.id == current_user.default_mailbox_id %} disabled {% endif %}"> Delete
Delete </span>
</span>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
<form method="post" class="mt-2"> <form method="post" class="mt-2">
{{ new_mailbox_form.csrf_token }} {{ new_mailbox_form.csrf_token }}
<input type="hidden" name="form-name" value="create"> <input type="hidden" name="form-name" value="create">
<h2 class="h4">New Mailbox</h2> <h2 class="h4">New Mailbox</h2>
{{ new_mailbox_form.email(class="form-control", placeholder="email@example.com") }} {{ new_mailbox_form.email(class="form-control", placeholder="email@example.com") }}
{{ render_field_errors(new_mailbox_form.email) }} {{ render_field_errors(new_mailbox_form.email) }}
<div class="small-text"> <div class="small-text">A mailbox can't be a disposable or forwarding email address.</div>
A mailbox can't be a disposable or forwarding email address.
</div>
<button class="btn btn-primary mt-2">Create</button> <button class="btn btn-primary mt-2">Create</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
$(".delete-mailbox").on("click", function (e) { $(".delete-mailbox").on("click", function (e) {
let mailbox = $(this).parent().find(".mailbox").val(); let mailbox = $(this).parent().find(".mailbox").val();
@ -154,4 +153,4 @@
}) })
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,12 +1,9 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "mailbox" %} {% set active_page = "mailbox" %}
{% block title %}Mailbox {{ mailbox.email }}{% endblock %}
{% block title %}
Mailbox {{ mailbox.email }}
{% endblock %}
{% block head %} {% block head %}
<style> <style>
div[disabled] div[disabled]
{ {
@ -15,51 +12,51 @@
} }
</style> </style>
{% endblock %} {% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h1 class="h3">{{ mailbox.email }} <h1 class="h3">
{{ mailbox.email }}
{% if mailbox.verified %} {% if mailbox.verified %}
<span class="cursor" data-toggle="tooltip" data-original-title="Mailbox Verified"></span>
<span class="cursor"
data-toggle="tooltip"
data-original-title="Mailbox Verified">✅</span>
{% else %} {% else %}
<span class="cursor" data-toggle="tooltip" data-original-title="Mailbox Not Verified"> <span class="cursor"
🚫 data-toggle="tooltip"
</span> data-original-title="Mailbox Not Verified">🚫</span>
{% endif %} {% endif %}
{% if mailbox.pgp_enabled() %} {% if mailbox.pgp_enabled() %}
<span class="cursor" data-toggle="tooltip" data-original-title="PGP Enabled">🗝</span>
<span class="cursor"
data-toggle="tooltip"
data-original-title="PGP Enabled">🗝</span>
{% endif %} {% endif %}
</h1> </h1>
{% if not mailbox.verified %} {% if not mailbox.verified %}
<div class="alert alert-info"> <div class="alert alert-info">
Mailbox not verified, please check your inbox/spam folder for the verification email. Mailbox not verified, please check your inbox/spam folder for the verification email.
<br> <br />
To receive the verification email again, you can delete and re-add the mailbox. To receive the verification email again, you can delete and re-add the mailbox.
</div> </div>
{% endif %} {% endif %}
<!-- Change email --> <!-- Change email -->
<div class="card"> <div class="card">
<form method="post" enctype="multipart/form-data"> <form method="post" enctype="multipart/form-data">
<input type="hidden" name="form-name" value="update-email"> <input type="hidden" name="form-name" value="update-email">
{{ change_email_form.csrf_token }} {{ change_email_form.csrf_token }}
<div class="card-body"> <div class="card-body">
<div class="card-title"> <div class="card-title">Change Mailbox Address</div>
Change Mailbox Address
</div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Address</label> <label class="form-label">Address</label>
<!-- Not allow user to change mailbox if there's a pending change --> <!-- Not allow user to change mailbox if there's a pending change -->
{{ change_email_form.email(class="form-control", value=mailbox.email, readonly=pending_email != None) }} {{ change_email_form.email(class="form-control", value=mailbox.email, readonly=pending_email != None) }}
{{ render_field_errors(change_email_form.email) }} {{ render_field_errors(change_email_form.email) }}
{% if pending_email %} {% if pending_email %}
<div class="mt-2"> <div class="mt-2">
<span class="text-danger">Pending change: {{ pending_email }}</span> <span class="text-danger">Pending change: {{ pending_email }}</span>
<a href="{{ url_for('dashboard.cancel_mailbox_change_route', mailbox_id=mailbox.id) }}" <a href="{{ url_for('dashboard.cancel_mailbox_change_route', mailbox_id=mailbox.id) }}"
@ -74,202 +71,163 @@
</form> </form>
</div> </div>
<!-- END Change email --> <!-- END Change email -->
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="card-title"> <div class="card-title">
<div class="d-flex"> <div class="d-flex">
Pretty Good Privacy (PGP) Pretty Good Privacy (PGP)
{% if mailbox.pgp_finger_print %} {% if mailbox.pgp_finger_print %}
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="toggle-pgp"> <input type="hidden" name="form-name" value="toggle-pgp">
<label class="custom-switch cursor" style="padding-left: 1rem" data-toggle="tooltip" {% if mailbox.disable_pgp %}
<label class="custom-switch cursor" style="padding-left: 1rem" title="Enable PGP" {% else %} title="Disable PGP" {% endif %}>
data-toggle="tooltip" <input type="checkbox" class="custom-switch-input" name="pgp-enabled" {{ "" if mailbox.disable_pgp else "checked" }}>
{% if mailbox.disable_pgp %}
title="Enable PGP"
{% else %}
title="Disable PGP"
{% endif %}
>
<input type="checkbox" class="custom-switch-input" name="pgp-enabled"
{{ "" if mailbox.disable_pgp else "checked" }}>
<span class="custom-switch-indicator"></span> <span class="custom-switch-indicator"></span>
</label> </label>
</form> </form>
{% endif %} {% endif %}
</div> </div>
<div class="small-text mt-1"> <div class="small-text mt-1">
By importing your PGP Public Key into SimpleLogin, all emails sent to {{ mailbox.email }} are By importing your PGP Public Key into SimpleLogin, all emails sent to {{ mailbox.email }} are
<b>encrypted</b> with your key. <br> <b>encrypted</b> with your key.
{% if PGP_SIGNER %} <br />
All forwarded emails will be signed with <b>{{ PGP_SIGNER }}</b>. {% if PGP_SIGNER %}All forwarded emails will be signed with <b>{{ PGP_SIGNER }}</b>.{% endif %}
{% endif %}
</div> </div>
</div> </div>
{% if not current_user.is_premium() %} {% if not current_user.is_premium() %}
<div class="alert alert-danger" role="alert">
This feature is only available in premium plan.
</div>
{% endif %}
<div class="alert alert-danger" role="alert">This feature is only available in premium plan.</div>
{% endif %}
<form method="post"> <form method="post">
<div class="form-group"> <div class="form-group">
<label class="form-label">PGP Public Key</label> <label class="form-label">PGP Public Key</label>
<textarea name="pgp" {% if not current_user.is_premium() %} disabled {% endif %} class="form-control" rows=10 id="pgp-public-key" placeholder="-----BEGIN PGP PUBLIC KEY BLOCK-----">{{ mailbox.pgp_public_key or "" }}</textarea>
<textarea name="pgp"
{% if not current_user.is_premium() %} disabled {% endif %}
class="form-control" rows=10 id="pgp-public-key"
placeholder="-----BEGIN PGP PUBLIC KEY BLOCK-----">{{ mailbox.pgp_public_key or "" }}</textarea>
</div> </div>
<input type="hidden" name="form-name" value="pgp"> <input type="hidden" name="form-name" value="pgp">
<button class="btn btn-primary" name="action" <button class="btn btn-primary" name="action" {% if not current_user.is_premium() %}
{% if not current_user.is_premium() %} disabled {% endif %} disabled {% endif %} value="save">
value="save">Save Save
</button> </button>
{% if mailbox.pgp_finger_print %} {% if mailbox.pgp_finger_print %}
<button class="btn btn-danger float-right" name="action" value="remove">Remove</button> <button class="btn btn-danger float-right" name="action" value="remove">Remove</button>
{% endif %} {% endif %}
</form> </form>
</div> </div>
</div> </div>
<div class="card" {% if not mailbox.pgp_enabled() %}
<div class="card" {% if not mailbox.pgp_enabled() %} disabled {% endif %}> disabled {% endif %}>
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="generic-subject"> <input type="hidden" name="form-name" value="generic-subject">
<div class="card-body"> <div class="card-body">
<div class="card-title"> <div class="card-title">
Hide email subject when PGP is enabled Hide email subject when PGP is enabled
<div class="small-text mt-1"> <div class="small-text mt-1">
When PGP is enabled, you can choose to use a <b>generic</b> subject for the forwarded emails. When PGP is enabled, you can choose to use a <b>generic</b> subject for the forwarded emails.
The original subject is then added into the email body. <br> The original subject is then added into the email body.
<br />
As PGP does not encrypt the email subject and the email subject might contain sensitive information, As PGP does not encrypt the email subject and the email subject might contain sensitive information,
this option will allow a further protection of your email content. this option will allow a further protection of your email content.
</div> </div>
</div> </div>
<div class="alert alert-info"> <div class="alert alert-info">
As the email is encrypted, a subject like "Email for you" As the email is encrypted, a subject like "Email for you"
will probably be rejected by your mailbox since it sounds like a spam. <br> will probably be rejected by your mailbox since it sounds like a spam.
<br />
Something like "Encrypted Email" would work much better :). Something like "Encrypted Email" would work much better :).
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Generic Subject</label> <label class="form-label">Generic Subject</label>
<input name="generic-subject" {% if not mailbox.pgp_enabled() %}
<input name="generic-subject" disabled {% endif %} class="form-control" maxlength="78" placeholder="Generic Subject" value="{{ mailbox.generic_subject or "" }}">
{% if not mailbox.pgp_enabled() %} disabled {% endif %}
class="form-control" maxlength="78"
placeholder="Generic Subject"
value="{{ mailbox.generic_subject or "" }}"
>
</div>
<button class="btn btn-primary" name="action"
{% if not mailbox.pgp_enabled() %} disabled {% endif %}
value="save">Save
</button>
{% if mailbox.generic_subject %}
<button class="btn btn-danger float-right" name="action" value="remove">Remove</button>
{% endif %}
</div>
</form>
</div>
<hr>
<h2 class="h4">Advanced Options</h2>
{% if spf_available %}
<div class="card" id="spf">
<form method="post">
<input type="hidden" name="form-name" value="force-spf">
<div class="card-body">
<div class="card-title">
Enforce SPF
<div class="small-text">
To avoid email-spoofing, SimpleLogin blocks email that
<em data-toggle="tooltip"
title="Email that has your mailbox as envelope-sender address">seems</em> to come from your
mailbox
but sent from <em data-toggle="tooltip"
title="IP Address that is not known by your mailbox email service">unknown</em>
IP address.
<br>
Only turn off this option if you know what you're doing :).
</div>
</div> </div>
<label class="custom-switch cursor mt-2 pl-0" <button class="btn btn-primary" name="action" {% if not mailbox.pgp_enabled() %}
data-toggle="tooltip" disabled {% endif %} value="save">
{% if mailbox.force_spf %} Save
title="Disable SPF enforcement" </button>
{% else %} {% if mailbox.generic_subject %}
title="Enable SPF enforcement"
{% endif %} <button class="btn btn-danger float-right" name="action" value="remove">Remove</button>
> {% endif %}
<input type="checkbox" name="spf-status" class="custom-switch-input"
{{ "checked" if mailbox.force_spf else "" }}>
<span class="custom-switch-indicator"></span>
</label>
</div> </div>
</form> </form>
</div> </div>
{% endif %} <hr />
<h2 class="h4">Advanced Options</h2>
{% if spf_available %}
<div class="card" id="authorized-address"> <div class="card" id="spf">
<div class="card-body"> <form method="post">
<div class="card-title"> <input type="hidden" name="form-name" value="force-spf">
Authorized addresses <div class="card-body">
<div class="small-text"> <div class="card-title">
Emails sent from these addresses to a <b>reverse-alias</b> are considered as being sent Enforce SPF
from {{ mailbox.email }} <div class="small-text">
</div> To avoid email-spoofing, SimpleLogin blocks email that
<em data-toggle="tooltip"
title="Email that has your mailbox as envelope-sender address">seems</em> to come from your
mailbox
but sent from <em data-toggle="tooltip"
title="IP Address that is not known by your mailbox email service">unknown</em>
IP address.
<br />
Only turn off this option if you know what you're doing :).
</div>
</div>
<label class="custom-switch cursor mt-2 pl-0" data-toggle="tooltip" {% if mailbox.force_spf %}
title="Disable SPF enforcement" {% else %} title="Enable SPF enforcement" {% endif %}>
<input type="checkbox" name="spf-status" class="custom-switch-input" {{ "checked" if mailbox.force_spf else "" }}>
<span class="custom-switch-indicator"></span>
</label>
</div>
</form>
</div> </div>
{% if mailbox.authorized_addresses | length == 0 %} {% endif %}
<div class="card" id="authorized-address">
<div class="card-body">
<div class="card-title">
Authorized addresses
<div class="small-text">
Emails sent from these addresses to a <b>reverse-alias</b> are considered as being sent
from {{ mailbox.email }}
</div>
</div>
{% if mailbox.authorized_addresses | length == 0 %}
{% else %} {% else %}
<ul> <ul>
{% for authorized_address in mailbox.authorized_addresses %} {% for authorized_address in mailbox.authorized_addresses %}
<li>
{{ authorized_address.email }}
<form method="post" action="#authorized-address" style="display: inline">
<input type="hidden" name="form-name" value="delete-authorized-address">
<input type="hidden" name="authorized-address-id" value="{{ authorized_address.id }}">
<input type="submit" class="btn btn-sm btn-outline-warning" value="Delete">
</form>
</li>
{% endfor %}
</ul>
{% endif %}
<form method="post" action="#authorized-address" class="form-inline"> <li>
<input type="hidden" name="form-name" value="add-authorized-address"> {{ authorized_address.email }}
<input type="email" name="email" size="50" class="form-control"> <form method="post" action="#authorized-address" style="display: inline">
<input type="submit" class="btn btn-primary" value="Add"> <input type="hidden" name="form-name" value="delete-authorized-address">
</form> <input type="hidden"
name="authorized-address-id"
value="{{ authorized_address.id }}">
<input type="submit" class="btn btn-sm btn-outline-warning" value="Delete">
</form>
</li>
{% endfor %}
</ul>
{% endif %}
<form method="post" action="#authorized-address" class="form-inline">
<input type="hidden" name="form-name" value="add-authorized-address">
<input type="email" name="email" size="50" class="form-control">
<input type="submit" class="btn btn-primary" value="Add">
</form>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> {% endblock %}
{% endblock %} {% block script %}
<script src="/static/js/utils/drag-drop-into-text.js"></script>
{% block script %} <script>
<script src="/static/js/utils/drag-drop-into-text.js"></script>
<script>
$(".custom-switch-input").change(function (e) { $(".custom-switch-input").change(function (e) {
$(this).closest("form").submit(); $(this).closest("form").submit();
}); });
enableDragDropForPGPKeys('#pgp-public-key'); enableDragDropForPGPKeys('#pgp-public-key');
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,19 +1,14 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block title %}Mailbox Validation{% endblock %}
{% block title %}
Mailbox Validation
{% endblock %}
{% block content %} {% block content %}
<div class="mx-auto p-7 card" style="max-width: 50rem">
<div class="text-center mb-7">Mailbox <b>{{ mailbox.email }}</b> verified, you can now start creating alias with it
</div>
<div class="mx-auto p-7 card" style="max-width: 50rem">
<div class="text-center mb-7">
Mailbox <b>{{ mailbox.email }}</b> verified, you can now start creating alias with it
</div>
<div class="mx-auto"> <div class="mx-auto">
<a href="{{ url_for('dashboard.index') }}" class="btn btn-primary">Go To Home Page</a> <a href="{{ url_for('dashboard.index') }}" class="btn btn-primary">Go To Home Page</a>
</div> </div>
</div> </div>
{% endblock %}
{% endblock %}

View File

@ -1,24 +1,19 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "setting" %} {% set active_page = "setting" %}
{% block title %} {% block title %}Cancel MFA{% endblock %}
Cancel MFA
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h2">Two Factor Authentication</h1> <h1 class="h2">Two Factor Authentication</h1>
<div> <div>
Disabling TOTP reduces the security of your account, please make sure to re-activate it later Disabling TOTP reduces the security of your account, please make sure to re-activate it later
or use WebAuthn (FIDO). or use WebAuthn (FIDO).
</div> </div>
<form method="post"> <form method="post">
<button class="btn btn-danger mt-2">Disable TOTP</button> <button class="btn btn-danger mt-2">Disable TOTP</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,22 +1,20 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "setting" %}
{% block title %}
MFA Setup
{% endblock %}
{% set active_page = "setting" %}
{% block title %}MFA Setup{% endblock %}
{% block head %} {% block head %}
<script src="{{ url_for('static', filename='node_modules/qrious/dist/qrious.min.js') }}"></script> <script src="{{ url_for('static', filename='node_modules/qrious/dist/qrious.min.js') }}"></script>
{% endblock %} {% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3">Two Factor Authentication - TOTP</h1> <h1 class="h3">Two Factor Authentication - TOTP</h1>
<p>You will need to use a 2FA application like Google Authenticator or Authy on your phone or PC and scan the following QR Code: <p>
You will need to use a 2FA application like Google Authenticator or Authy on your phone or PC and scan the following QR Code:
</p> </p>
<canvas id="qr"></canvas> <canvas id="qr"></canvas>
<script> <script>
(function () { (function () {
var qr = new QRious({ var qr = new QRious({
@ -25,25 +23,16 @@
}); });
})(); })();
</script> </script>
<div class="mt-3 mb-2">Or you can manually enter the following secret key:</div>
<div class="mt-3 mb-2">
Or you can manually enter the following secret key:
</div>
<input class="form-control" disabled value="{{ current_user.otp_secret }}"> <input class="form-control" disabled value="{{ current_user.otp_secret }}">
<form method="post"> <form method="post">
{{ otp_token_form.csrf_token }} {{ otp_token_form.csrf_token }}
<div class="font-weight-bold mt-5">Token</div> <div class="font-weight-bold mt-5">Token</div>
<div class="small-text">Please enter the 6-digit number displayed in your authenticator app.</div> <div class="small-text">Please enter the 6-digit number displayed in your authenticator app.</div>
{{ otp_token_form.token(class="form-control", placeholder="") }} {{ otp_token_form.token(class="form-control", placeholder="") }}
{{ render_field_errors(otp_token_form.token) }} {{ render_field_errors(otp_token_form.token) }}
<button class="btn btn-lg btn-success mt-2">Submit</button> <button class="btn btn-lg btn-success mt-2">Submit</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,44 +1,38 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% block title %}
API Key
{% endblock %}
{% block title %}API Key{% endblock %}
{% set active_page = "api_key" %} {% set active_page = "api_key" %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3">New API Key {{ api_key.name }} is created </h1> <h1 class="h3">New API Key {{ api_key.name }} is created</h1>
<div class="alert alert-warning">For security reasons, API Key is only visible when it is created.</div>
<div class="alert alert-warning">
For security reasons, API Key is only visible when it is created.
</div>
<div class="input-group mb-2"> <div class="input-group mb-2">
<input class="form-control" id="apikey-{{ api_key.id }}" readonly value="**********"> <input class="form-control"
id="apikey-{{ api_key.id }}"
readonly
value="**********">
<div class="input-group-append"> <div class="input-group-append">
<span class="input-group-text"> <span class="input-group-text">
<i class="fe fe-eye toggle-api-key" data-show="off" data-secret="{{ api_key.code }}" <i class="fe fe-eye toggle-api-key"
></i> data-show="off"
</span> data-secret="{{ api_key.code }}"></i>
</span>
</div> </div>
</div> </div>
<button class="clipboard btn btn-primary"
<button class="clipboard btn btn-primary" data-clipboard-action="copy" data-clipboard-action="copy"
data-clipboard-text="{{ api_key.code }}" data-clipboard-text="{{ api_key.code }}"
data-clipboard-target="#apikey-{{ api_key.id }}"> data-clipboard-target="#apikey-{{ api_key.id }}">
Copy &nbsp; &nbsp; <i class="fe fe-clipboard"></i> Copy &nbsp; &nbsp; <i class="fe fe-clipboard"></i>
</button> </button>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script>
<script>
$(".toggle-api-key").on('click', function (event) { $(".toggle-api-key").on('click', function (event) {
let that = $(this); let that = $(this);
let apiInput = that.parent().parent().parent().find("input"); let apiInput = that.parent().parent().parent().find("input");

View File

@ -1,29 +1,18 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}{{ notification.title }}{% endblock %}
{% block title %}
{{ notification.title }}
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3"> <h1 class="h3">{{ notification.title or "" }}</h1>
{{ notification.title or "" }} <div>{{ notification.message | safe }}</div>
</h1> <form method="post"
class="float-right mt-3"
<div>
{{ notification.message | safe }}
</div>
<form method="post" class="float-right mt-3"
onsubmit="return confirm('This operation is not reversible, please confirm');"> onsubmit="return confirm('This operation is not reversible, please confirm');">
<button class="btn btn-outline-danger">Delete</button> <button class="btn btn-outline-danger">Delete</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,42 +1,36 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}Notifications{% endblock %}
{% block title %}
Notifications
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h1 class="h3">Notifications</h1>
<h1 class="h3"> Notifications </h1>
{% for notification in notifications %} {% for notification in notifications %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="h4"> <div class="h4">{{ notification.title or "" }}</div>
{{ notification.title or ""}} <div style="width: 40em;
</div> word-wrap:break-word;
white-space: normal;
<div overflow: hidden;
style="width: 40em; word-wrap:break-word; white-space: normal; overflow: hidden; max-height: 100px; text-overflow: ellipsis;"> max-height: 100px;
text-overflow: ellipsis;">
{{ notification.message | safe }} {{ notification.message | safe }}
</div> </div>
<a href="{{ url_for('dashboard.notification_route', notification_id=notification.id) }}" <a href="{{ url_for('dashboard.notification_route', notification_id=notification.id) }}"
class="mt-2 btn btn-outline-primary">More ➡</a> class="mt-2 btn btn-outline-primary">
More ➡
<div class="small text-muted mt-2"> </a>
{{ notification.created_at | dt }} <div class="small text-muted mt-2">{{ notification.created_at | dt }}</div>
</div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
<!-- Only show pagination control if there are previous/next page --> <!-- Only show pagination control if there are previous/next page -->
{% if page > 0 or not last_page %} {% if page > 0 or not last_page %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<nav aria-label="Notification navigation"> <nav aria-label="Notification navigation">
@ -44,21 +38,20 @@
<li class="page-item mr-1"> <li class="page-item mr-1">
<a class="btn btn-outline-primary {% if page == 0 %}disabled{% endif %}" <a class="btn btn-outline-primary {% if page == 0 %}disabled{% endif %}"
href="{{ url_for('dashboard.notifications_route', page=page-1) }}"> href="{{ url_for('dashboard.notifications_route', page=page-1) }}">
Previous</a> Previous
</a>
</li> </li>
<li class="page-item"> <li class="page-item">
<a class="btn btn-outline-primary {% if last_page %}disabled{% endif %}" <a class="btn btn-outline-primary {% if last_page %}disabled{% endif %}"
href="{{ url_for('dashboard.notifications_route', page=page+1) }}"> href="{{ url_for('dashboard.notifications_route', page=page+1) }}">
Next</a> Next
</a>
</li> </li>
</ul> </ul>
</nav> </nav>
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,21 +1,17 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}Pricing{% endblock %}
{% block title %}
Pricing
{% endblock %}
{% block head %} {% block head %}
<script src="https://cdn.paddle.com/paddle/paddle.js"></script> <script src="https://cdn.paddle.com/paddle/paddle.js"></script>
<script> <script>
if (window.Paddle === undefined) { if (window.Paddle === undefined) {
console.log("cannot load Paddle from CDN"); console.log("cannot load Paddle from CDN");
document.write('<script src="/static/vendor/paddle.js"><\/script>') document.write('<script src="/static/vendor/paddle.js"><\/script>')
} }
</script> </script>
<style type="text/css">
<style type="text/css">
html.mvc__a.mvc__lot.mvc__of.mvc__classes.mvc__to.mvc__increase.mvc__the.mvc__odds.mvc__of.mvc__winning.mvc__specificity, html.mvc__a.mvc__lot.mvc__of.mvc__classes.mvc__to.mvc__increase.mvc__the.mvc__odds.mvc__of.mvc__winning.mvc__specificity > body { html.mvc__a.mvc__lot.mvc__of.mvc__classes.mvc__to.mvc__increase.mvc__the.mvc__odds.mvc__of.mvc__winning.mvc__specificity, html.mvc__a.mvc__lot.mvc__of.mvc__classes.mvc__to.mvc__increase.mvc__the.mvc__odds.mvc__of.mvc__winning.mvc__specificity > body {
position: static; position: static;
} }
@ -29,175 +25,177 @@
[data-toggle="collapse"]:not(.collapsed) .if-collapsed { [data-toggle="collapse"]:not(.collapsed) .if-collapsed {
display: none; display: none;
} }
</style> </style>
{% endblock %} {% endblock %}
{% block announcement %} {% block announcement %}
{# TODO: to remove#}
{# <div class="alert alert-danger text-center mb-0" role="alert">#} {# TODO: to remove#}
{# Our payment provider Paddle is experiencing#} {# <div class="alert alert-danger text-center mb-0" role="alert">#}
{# <a href="https://paddle.status.io" target="_blank">server issue <i class="fe fe-external-link"></i></a>#} {# Our payment provider Paddle is experiencing#}
{# that can make our checkout page unusable. <br>#} {# <a href="https://paddle.status.io" target="_blank">server issue <i class="fe fe-external-link"></i></a>#}
{# Please retry later and sorry for this issue!#} {# that can make our checkout page unusable. <br />#}
{# </div>#} {# Please retry later and sorry for this issue!#}
{# </div>#}
{% endblock %} {% endblock %}
{% block default_content %} {% block default_content %}
<div class="row">
<div class="col-sm-6 col-lg-6">
<div class="card">
<div class="card-body text-center">
<div class="h3">Premium</div>
<ul class="list-unstyled leading-loose mb-3"> <div class="row">
<li><i class="fe fe-check text-success mr-2" aria-hidden="true"></i> <div class="col-sm-6 col-lg-6">
Unlimited aliases <div class="card">
</li> <div class="card-body text-center">
<li><i class="fe fe-check text-success mr-2" aria-hidden="true"></i> <div class="h3">Premium</div>
Unlimited custom domains <ul class="list-unstyled leading-loose mb-3">
</li> <li>
<li><i class="fe fe-check text-success mr-2" aria-hidden="true"></i> <i class="fe fe-check text-success mr-2" aria-hidden="true"></i>
Catch-all (or wildcard) aliases Unlimited aliases
</li> </li>
<li><i class="fe fe-check text-success mr-2" aria-hidden="true"></i> <li>
Up to 50 directories (or usernames) <i class="fe fe-check text-success mr-2" aria-hidden="true"></i>
</li> Unlimited custom domains
<li><i class="fe fe-check text-success mr-2" aria-hidden="true"></i> </li>
Unlimited mailboxes <li>
</li> <i class="fe fe-check text-success mr-2" aria-hidden="true"></i>
<li><i class="fe fe-check text-success mr-2" aria-hidden="true"></i> Catch-all (or wildcard) aliases
PGP Encryption </li>
</li> <li>
</ul> <i class="fe fe-check text-success mr-2" aria-hidden="true"></i>
Up to 50 directories (or usernames)
</li>
<li>
<i class="fe fe-check text-success mr-2" aria-hidden="true"></i>
Unlimited mailboxes
</li>
<li>
<i class="fe fe-check text-success mr-2" aria-hidden="true"></i>
PGP Encryption
</li>
</ul>
<div class="small-text">
More information on our
<a href="https://simplelogin.io/pricing" target="_blank" rel="noopener">
Pricing
Page <i class="fe fe-external-link"></i>
</a>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-lg-6">
{% if manual_sub %}
<div class="small-text">More information on our <a href="https://simplelogin.io/pricing" target="_blank" <div class="alert alert-info">
rel="noopener">Pricing You currently have a subscription until <b>{{ manual_sub.end_at.format("YYYY-MM-DD") }}</b>
Page <i class="fe fe-external-link"></i> ({{ (manual_sub.end_at - now).days }} days left).
</a></div> <br />
</div> Please note that the time left will <b>not</b> be taken into account in a new subscription.
</div> </div>
</div> <hr />
{% endif %}
{% if proton_upgrade %}
<div class="col-sm-6 col-lg-6"> <div id="proton-upgrade">
{% if manual_sub %} <h4>Proton Unlimited and Mail Plus plans include SimpleLogin premium and more!</h4>
<div class="alert alert-info"> <a class="btn btn-primary" role="button" href="https://account.proton.me/u/0/mail/upgrade">
You currently have a subscription until <b>{{ manual_sub.end_at.format("YYYY-MM-DD") }}</b> <b>Upgrade your Proton account</b>
({{ (manual_sub.end_at - now).days }} days left). </a>
<br> <p class="mt-2 small">
Please note that the time left will <b>not</b> be taken into account in a new subscription. Starts at $3.99/month (billed yearly), starting with 15GB of storage, encrypted
</div> calendar & file storage and more.
<hr> </p>
{% endif %} <div class="middle-line my-5 h4">OR</div>
<div id="normal-upgrade-button">
<a class="btn btn-secondary collapsed" data-toggle="collapse" href="#normal-upgrade" role="button">
Upgrade your SimpleLogin account
<span class="if-collapsed">
<i class="fe fe-chevron-down"></i>
</span>
<span class="if-not-collapsed">
<i class="fe fe-chevron-up"></i>
</span>
</a>
<p class="mt-2 small">
Starts at $2.5/month (billed yearly)
</p>
</div>
</div>
{% endif %}
<div id="normal-upgrade" class="{% if proton_upgrade %} collapse{% endif %}">
<div class="display-6 my-3">
🔐 Secure payments by
<a href="https://paddle.com" target="_blank" rel="noopener">
Paddle <i class="fe fe-external-link"></i>
</a>
</div>
{% set sub = current_user.get_paddle_subscription() %}
{% if sub and sub.cancelled %}
{% if proton_upgrade %} <div class="alert alert-primary" role="alert">
<div id="proton-upgrade" class=""> You have an active subscription until {{ sub.next_bill_date.strftime("%Y-%m-%d") }}.
<h4 class="">Proton Unlimited and Mail Plus plans include SimpleLogin premium and more!</h4> <br />
<a class="btn btn-primary" role="button" href="https://account.proton.me/u/0/mail/upgrade"> Please note that if you re-subscribe now, this will be a completely
<b>Upgrade your Proton account</b> new subscription and
</a> your payment method will be charged <b>immediately</b>.
<p class="mt-2 small"> Starts at $3.99/month (billed yearly), starting with 15GB of storage, encrypted </div>
calendar & file storage and more. </p> {% endif %}
{% if coinbase_sub %}
<div class="middle-line my-5 h4">OR</div> <div class="alert alert-info">
You currently have a Coinbase subscription until <b>{{ coinbase_sub.end_at.format("YYYY-MM-DD") }}</b>
<div id="normal-upgrade-button"> ({{ (coinbase_sub.end_at - now).days }} days left).
<a class="btn btn-secondary collapsed" data-toggle="collapse" href="#normal-upgrade" role="button"> <br />
Upgrade your SimpleLogin account Please note that the time left will <b>not</b> be taken into account in a new Paddle subscription.
<span class="if-collapsed"> </div>
<i class="fe fe-chevron-down"></i> {% endif %}
</span> <div class="mb-3">
<span class="if-not-collapsed"> Paddle supports bank cards
<i class="fe fe-chevron-up"></i> (Mastercard, Visa, American Express, etc) and PayPal.
</span> </div>
</a> <button class="btn btn-primary" onclick="upgrade({{ PADDLE_YEARLY_PRODUCT_ID }})">
<p class="mt-2 small">Starts at $2.5/month (billed yearly)</p> Yearly billing
</div> <span class="badge badge-success">Save $18</span>
</div> <br />
{% endif %} <span style="font-size: 18px">$30/year</span>
</button>
<div id="normal-upgrade" class="{% if proton_upgrade %} collapse {% endif %}"> <button class="btn btn-secondary" onclick="upgrade({{ PADDLE_MONTHLY_PRODUCT_ID }})">
Monthly billing
<div class="display-6 my-3"> <br />
🔐 Secure payments by <b>
<a href="https://paddle.com" target="_blank" rel="noopener"> $4/month
Paddle <i class="fe fe-external-link"></i> </b>
</a> </button>
</div> <hr />
<i class="fa fa-bitcoin"></i>
{% set sub = current_user.get_paddle_subscription() %} Payment via
{% if sub and sub.cancelled %} <a href="https://commerce.coinbase.com/?lang=en" target="_blank">
<div class="alert alert-primary" role="alert"> Coinbase Commerce<i class="fe fe-external-link"></i>
You have an active subscription until {{ sub.next_bill_date.strftime("%Y-%m-%d") }}. <br> </a>
Please note that if you re-subscribe now, this will be a completely <br />
new subscription and Currently Bitcoin, Bitcoin Cash, Dai, Ethereum, Litecoin and USD Coin are supported.
your payment method will be charged <b>immediately</b>. <br />
</div> <a class="btn btn-outline-primary" href="{{ url_for('dashboard.coinbase_checkout_route') }}" target="_blank">
{% endif %} Yearly billing - Crypto
<br />
{% if coinbase_sub %} $30/year
<div class="alert alert-info"> <i class="fe fe-external-link"></i>
You currently have a Coinbase subscription until <b>{{ coinbase_sub.end_at.format("YYYY-MM-DD") }}</b> </a>
({{ (coinbase_sub.end_at - now).days }} days left). <hr />
<br> For other payment options, please send us an email at
Please note that the time left will <b>not</b> be taken into account in a new Paddle subscription. <a href="mailto:hi@simplelogin.io">hi@simplelogin.io</a>
</div> .
{% endif %} <br />
If you have bought a coupon, please go to the
<div class="mb-3"> <a href="{{ url_for('dashboard.coupon_route') }}">coupon page</a>
Paddle supports bank cards to apply the coupon code.
(Mastercard, Visa, American Express, etc) and PayPal. </div>
</div> </div>
</div>
<button class="btn btn-primary" onclick="upgrade({{ PADDLE_YEARLY_PRODUCT_ID }})"> <script type="text/javascript">
Yearly billing
<span class="badge badge-success">Save $18</span>
<br>
<span style="font-size: 18px">
$30/year
</span>
</button>
<button class="btn btn-secondary" onclick="upgrade({{ PADDLE_MONTHLY_PRODUCT_ID }})">
Monthly billing <br>
<b>
$4/month
</b>
</button>
<hr>
<i class="fa fa-bitcoin"></i>
Payment via
<a href="https://commerce.coinbase.com/?lang=en" target="_blank">
Coinbase Commerce<i class="fe fe-external-link"></i>
</a> <br>
Currently Bitcoin, Bitcoin Cash, Dai, Ethereum, Litecoin and USD Coin are supported. <br>
<a class="btn btn-outline-primary" href="{{ url_for('dashboard.coinbase_checkout_route') }}"
target="_blank">
Yearly billing - Crypto <br>
$30/year
<i class="fe fe-external-link"></i>
</a>
<hr>
For other payment options, please send us an email at <a href="mailto:hi@simplelogin.io">hi@simplelogin.io</a>.
<br>
If you have bought a coupon, please go to the <a href="{{ url_for('dashboard.coupon_route') }}">coupon page</a>
to apply the coupon code.
</div>
</div>
</div>
<script type="text/javascript">
Paddle.Setup({vendor: {{ PADDLE_VENDOR_ID }}}); Paddle.Setup({vendor: {{ PADDLE_VENDOR_ID }}});
function upgrade(productId) { function upgrade(productId) {
bootbox.dialog({ bootbox.dialog({
title: `Payment with credit card or PayPal via Paddle`, title: `Payment with credit card or PayPal via Paddle`,
message: `Paddle will ask for an email address for sending out the invoices, please feel free to use an alias. <br> message: `Paddle will ask for an email address for sending out the invoices, please feel free to use an alias. <br />
You don't have to use your SimpleLogin account email address`, You don't have to use your SimpleLogin account email address`,
size: 'large', size: 'large',
onEscape: true, onEscape: true,
@ -218,6 +216,5 @@
}); });
} }
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,10 +1,9 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "setting" %}
{% block title %}
Recovery Codes
{% endblock %}
{% set active_page = "setting" %}
{% block title %}Recovery Codes{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3">Recovery codes</h1> <h1 class="h3">Recovery codes</h1>
@ -13,34 +12,29 @@
your account again. your account again.
Each code can only be used once, make sure to store them in a safe place. Each code can only be used once, make sure to store them in a safe place.
</p> </p>
<ul> <ul>
{% for recovery_code in recovery_codes %} {% for recovery_code in recovery_codes %}
{% if recovery_code.used %} {% if recovery_code.used %}
<li> <li>
<span style="text-decoration: line-through">{{ recovery_code.code }}</span>. <span style="text-decoration: line-through">{{ recovery_code.code }}</span>.
Used {{ recovery_code.used_at | dt }}. Used {{ recovery_code.used_at | dt }}.
</li> </li>
{% else %} {% else %}
<li> <li>{{ recovery_code.code }}</li>
{{ recovery_code.code }}
</li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</ul> </ul>
<form method="post" class="mt-6"> <form method="post" class="mt-6">
<input type="submit" class="btn btn-outline-primary" value="Generate New Codes"> <input type="submit"
class="btn btn-outline-primary"
value="Generate New Codes">
</form> </form>
<div class="small-text"> <div class="small-text">Warning: Generating new codes will invalidate the older ones.</div>
Warning: Generating new codes will invalidate the older ones. <hr />
</div> <a href="{{ url_for('dashboard.index') }}"
class="btn btn-primary btn-lg">Back to the home page</a>
<hr>
<a href="{{ url_for('dashboard.index') }}" class="btn btn-primary btn-lg"> Back to the home page </a>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,76 +1,92 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% block title %}
Referral
{% endblock %}
{% block title %}Referral{% endblock %}
{% set active_page = "setting" %} {% set active_page = "setting" %}
{% block default_content %} {% block default_content %}
<div class="col"> <div class="col">
<h1 class="h3 mb-5"> Referrals <h1 class="h3 mb-5">
<a class="ml-3 text-info" style="font-size: 12px" data-toggle="collapse" href="#howtouse" role="button" Referrals
aria-expanded="false" aria-controls="collapseExample"> <a class="ml-3 text-info"
style="font-size: 12px"
data-toggle="collapse"
href="#howtouse"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
How to use <i class="fe fe-chevrons-down"></i> How to use <i class="fe fe-chevrons-down"></i>
</a> </a>
</h1> </h1>
<div class="alert alert-primary collapse" id="howtouse" role="alert"> <div class="alert alert-primary collapse" id="howtouse" role="alert">
On this page, you can create a <b>referral code</b> that you can use when referring people to SimpleLogin. On this page, you can create a <b>referral code</b> that you can use when referring people to SimpleLogin.
For every user who <b>upgrades</b> and stays with us at least 3 months, you'll get $5 :). <br> For every user who <b>upgrades</b> and stays with us at least 3 months, you'll get $5 :).
<br />
The payout can be initiated any time, just send us an email at The payout can be initiated any time, just send us an email at
<a href="mailto:hi@simplelogin.io">hi@simplelogin.io</a> when you want to receive the payout. <a href="mailto:hi@simplelogin.io">hi@simplelogin.io</a>
when you want to receive the payout.
</div> </div>
{% if referrals|length == 0 %} {% if referrals|length == 0 %}
<div class="alert alert-info"> <div class="alert alert-info">
You don't have any referral code yet. Let's create the first one and start inviting your friends! <br> You don't have any referral code yet. Let's create the first one and start inviting your friends!
<br />
</div> </div>
{% endif %} {% endif %}
{% for referral in referrals %} {% for referral in referrals %}
<div class="card p-4 shadow-sm {% if referral.id == highlight_id %} highlight-row {% endif %}">
<div class="card p-4 shadow-sm {% if referral.id == highlight_id %} highlight-row{% endif %}">
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="update"> <input type="hidden" name="form-name" value="update">
<input type="hidden" name="referral-id" value="{{ referral.id }}"> <input type="hidden" name="referral-id" value="{{ referral.id }}">
<div class="d-flex mb-3"> <div class="d-flex mb-3">
<div class="mr-2"> <div class="mr-2">
<input name="name" class="form-control" required value="{{ referral.name or '' }}"> <input name="name"
class="form-control"
required
value="{{ referral.name or '' }}">
</div> </div>
<div> <div>
<button class="btn btn-outline-success">Update</button> <button class="btn btn-outline-success">Update</button>
</div> </div>
</div> </div>
</form> </form>
{% set nb_user = referral.nb_user %} {% set nb_user = referral.nb_user %}
{% set nb_paid_user = referral.nb_paid_user %} {% set nb_paid_user = referral.nb_paid_user %}
{% if nb_user > 0 %} {% if nb_user > 0 %}
<div class="mb-3"> <div class="mb-3">
<b class="h1">{{ nb_user }}</b> <b class="h1">{{ nb_user }}</b>
{% if nb_user == 1 %} person {% else %} people {% endif %} {% if nb_user == 1 %}
has their online privacy protected thanks to you! <br>
person
{% else %}
people
{% endif %}
has their online privacy protected thanks to you!
<br />
Among them, <b class="h1">{{ nb_paid_user }}</b> Among them, <b class="h1">{{ nb_paid_user }}</b>
{% if nb_paid_user == 1 %} person {% else %} people {% endif %} {% if nb_paid_user == 1 %}
person
{% else %}
people
{% endif %}
has upgraded their accounts. has upgraded their accounts.
</div> </div>
{% endif %} {% endif %}
<div class="mt-2"> <div class="mt-2">
Please use this referral link to invite your friends trying out SimpleLogin: <br> Please use this referral link to invite your friends trying out SimpleLogin:
<br />
<div class="d-flex mb-5 mt-2" style="max-width: 40em"> <div class="d-flex mb-5 mt-2" style="max-width: 40em">
<div class="flex-grow-1 mr-2"> <div class="flex-grow-1 mr-2">
<input class="form-control" id="referral-{{ referral.id }}" readonly <input class="form-control"
id="referral-{{ referral.id }}"
readonly
value="{{ referral.link() }}"> value="{{ referral.link() }}">
</div> </div>
<div> <div>
<button class="clipboard btn btn-outline-primary" data-clipboard-action="copy" <button class="clipboard btn btn-outline-primary"
data-clipboard-action="copy"
data-clipboard-text="{{ referral.link() }}" data-clipboard-text="{{ referral.link() }}"
data-clipboard-target="#referral-{{ referral.id }}"> data-clipboard-target="#referral-{{ referral.id }}">
Copy <i class="fe fe-clipboard"></i> Copy <i class="fe fe-clipboard"></i>
@ -78,19 +94,19 @@
</div> </div>
</div> </div>
</div> </div>
<div class="mb-3"> <div class="mb-3">
You can also use the referral code <b>{{ referral.code }}</b> when sharing any link on SimpleLogin. <br> You can also use the referral code <b>{{ referral.code }}</b> when sharing any link on SimpleLogin.
<br />
Just append Just append
<em data-toggle="tooltip" <em data-toggle="tooltip"
title="Click to copy" title="Click to copy"
class="clipboard" class="clipboard"
data-clipboard-text="{{ '?slref=' + referral.code }}" style="overflow-wrap: break-word"> data-clipboard-text="{{ '?slref=' + referral.code }}"
style="overflow-wrap: break-word">
?slref={{ referral.code }} ?slref={{ referral.code }}
</em> </em>
to any link on SimpleLogin website. to any link on SimpleLogin website.
</div> </div>
<div> <div>
<form method="post"> <form method="post">
<input type="hidden" name="form-name" value="delete"> <input type="hidden" name="form-name" value="delete">
@ -98,15 +114,13 @@
<span class="delete-referral float-right btn btn-outline-danger">Delete</span> <span class="delete-referral float-right btn btn-outline-danger">Delete</span>
</form> </form>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
<form method="post" class="mt-6 card p-4 shadow"> <form method="post" class="mt-6 card p-4 shadow">
<input type="hidden" name="form-name" value="create"> <input type="hidden" name="form-name" value="create">
<div class="form-group"> <div class="form-group">
<input name="code" class="form-control" <input name="code"
class="form-control"
pattern="[0-9a-z-_]{3,}" pattern="[0-9a-z-_]{3,}"
placeholder="Referral Code" placeholder="Referral Code"
title="At least 3 characters. Only lowercase letters, numbers, dashes (-) and underscores (_) are currently supported."> title="At least 3 characters. Only lowercase letters, numbers, dashes (-) and underscores (_) are currently supported.">
@ -115,47 +129,45 @@
dashes (-) and underscores (_) are currently supported. dashes (-) and underscores (_) are currently supported.
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<input name="name" class="form-control" required <input name="name"
class="form-control"
required
placeholder="Referral name, something to help you remember why you create it :)"> placeholder="Referral name, something to help you remember why you create it :)">
</div> </div>
<button class="btn btn-success mb-5">Create</button> <button class="btn btn-success mb-5">Create</button>
</form> </form>
{% if payouts|length > 0 %} {% if payouts|length > 0 %}
<div class="mt-6 card p-4 shadow"> <div class="mt-6 card p-4 shadow">
<h3 class="h4">Payouts</h3> <h3 class="h4">Payouts</h3>
<table class="table"> <table class="table">
<thead> <thead>
<tr>
<th>Sent at</th>
<th>Amount</th>
<th>Payment Method</th>
<th>Number of upgraded accounts</th>
</tr>
</thead>
<tbody>
{% for payout in payouts %}
<tr> <tr>
<td>{{ payout.created_at | dt }}</td> <th>Sent at</th>
<td>${{ payout.amount }}</td> <th>Amount</th>
<td>{{ payout.payment_method }}</td> <th>Payment Method</th>
<td>{{ payout.number_upgraded_account }}</td> <th>Number of upgraded accounts</th>
</tr> </tr>
{% endfor %} </thead>
<tbody>
{% for payout in payouts %}
<tr>
<td>{{ payout.created_at | dt }}</td>
<td>${{ payout.amount }}</td>
<td>{{ payout.payment_method }}</td>
<td>{{ payout.number_upgraded_account }}</td>
</tr>
{% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
$(".delete-referral").on("click", function (e) { $(".delete-referral").on("click", function (e) {
let that = $(this); let that = $(this);
@ -182,4 +194,4 @@
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,69 +1,55 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% block title %}
Quarantine
{% endblock %}
{% block title %}Quarantine{% endblock %}
{% set active_page = "setting" %} {% set active_page = "setting" %}
{% block default_content %} {% block default_content %}
<div class="col">
<h1 class="h3 mb-5"> Quarantine </h1>
<div class="col">
<h1 class="h3 mb-5">Quarantine</h1>
<div class="alert alert-info"> <div class="alert alert-info">
This page shows all emails that are potentially spams or malicious. This page shows all emails that are potentially spams or malicious.
Usually these emails have been <b>refused</b> (or bounced) by your mailbox. <br> Usually these emails have been <b>refused</b> (or bounced) by your mailbox.
<br />
- If an email is indeed spam, this means the alias is now in the hands of a spammer, - If an email is indeed spam, this means the alias is now in the hands of a spammer,
in this case you should <b>disable</b> this alias. <br> in this case you should <b>disable</b> this alias.
<br />
- Otherwise, you should create a <b>filter</b> to avoid your email provider from blocking these emails. <br> - Otherwise, you should create a <b>filter</b> to avoid your email provider from blocking these emails.
<a href="mailto:hi@simplelogin.io">Contact us↗</a> if you need any help. <br />
<a href="mailto:hi@simplelogin.io">Contact us↗</a>
if you need any help.
</div> </div>
{% if email_logs|length == 0 %}<div class="my-4 p-4 card">You don't have any emails in Quarantine.</div>{% endif %}
{% if email_logs|length == 0 %}
<div class="my-4 p-4 card">
You don't have any emails in Quarantine.
</div>
{% endif %}
{% for email_log in email_logs %} {% for email_log in email_logs %}
{% set refused_email = email_log.refused_email %} {% set refused_email = email_log.refused_email %}
{% set contact = email_log.contact %} {% set contact = email_log.contact %}
{% set alias = contact.alias %} {% set alias = contact.alias %}
<div class="card p-4 shadow-sm {% if email_log.id == highlight_id %} highlight-row{% endif %}">
<div class="card p-4 shadow-sm {% if email_log.id == highlight_id %} highlight-row {% endif %}"> <div class="small-text">Sent {{ refused_email.created_at | dt }}</div>
<div class="small-text">
Sent {{ refused_email.created_at | dt }}
</div>
{% if email_log.is_reply %} {% if email_log.is_reply %}
From: {{ alias.email }} <br>
From: {{ alias.email }}
<br />
To: {{ contact.website_email }} To: {{ contact.website_email }}
{% else %} {% else %}
From: {{ contact.website_email }} <br> From: {{ contact.website_email }}
<br />
<span> <span>
To: {{ alias.email }} To: {{ alias.email }}
<a href='{{ url_for("dashboard.index", highlight_alias_id=alias.id) }}' <a href='{{ url_for("dashboard.index", highlight_alias_id=alias.id) }}'
class="btn btn-sm btn-outline-danger">Disable Alias</a> class="btn btn-sm btn-outline-danger">
</span> Disable Alias
</a>
</span>
{% endif %} {% endif %}
{% if refused_email.deleted %} {% if refused_email.deleted %}
<div>
Email deleted {{ refused_email.delete_at | dt }} <div>Email deleted {{ refused_email.delete_at | dt }}</div>
</div>
{% else %} {% else %}
<a href="{{ refused_email.get_url() }}" download <a href="{{ refused_email.get_url() }}" download class="mt-4">Download →</a>
class="mt-4">Download →</a>
<div class="small-text">This will download a ".eml" file that you can open in your email client</div> <div class="small-text">This will download a ".eml" file that you can open in your email client</div>
{% endif %} {% endif %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% endblock %} {% endblock %}

File diff suppressed because it is too large Load Diff

View File

@ -1,78 +1,78 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "subdomain" %} {% set active_page = "subdomain" %}
{% block title %}Subdomains{% endblock %}
{% block title %} {% block head %}{% endblock %}
Subdomains
{% endblock %}
{% block head %}
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h1 class="h3"> Subdomains <h1 class="h3">
<a class="ml-3 text-info" style="font-size: 12px" data-toggle="collapse" href="#howtouse" role="button" Subdomains
aria-expanded="false" aria-controls="collapseExample"> <a class="ml-3 text-info"
style="font-size: 12px"
data-toggle="collapse"
href="#howtouse"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
How to use <i class="fe fe-chevrons-down"></i> How to use <i class="fe fe-chevrons-down"></i>
</a> </a>
</h1> </h1>
{% if not current_user.is_premium() %} {% if not current_user.is_premium() %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
This feature is only available on Premium plan. This feature is only available on Premium plan.
<a href="{{ url_for('dashboard.pricing') }}" target="_blank" rel="noopener"> <a href="{{ url_for('dashboard.pricing') }}"
target="_blank"
rel="noopener">
Upgrade<i class="fe fe-external-link"></i> Upgrade<i class="fe fe-external-link"></i>
</a> </a>
</div> </div>
{% endif %} {% endif %}
<div class="alert alert-primary collapse {% if not subdomains %} show{% endif %}"
<div class="alert alert-primary collapse {% if not subdomains %} show {% endif %}" id="howtouse" role="alert"> id="howtouse"
You can use subdomain to quickly create email aliases without opening SimpleLogin app. <br> role="alert">
You can use subdomain to quickly create email aliases without opening SimpleLogin app.
<br />
Handy when you need to quickly give out an email address, for example on a phone call, in a meeting or just Handy when you need to quickly give out an email address, for example on a phone call, in a meeting or just
anywhere you want. <br> anywhere you want.
<br />
After choosing a subdomain, simply use <b>anything@my-subdomain.simplelogin.co</b> After choosing a subdomain, simply use <b>anything@my-subdomain.simplelogin.co</b>
next time you need an alias: next time you need an alias:
it'll be <b>automatically created</b> the first time it receives an email. <br> it'll be <b>automatically created</b> the first time it receives an email.
<br />
</div> </div>
<div class="row"> <div class="row">
{% for subdomain in subdomains %} {% for subdomain in subdomains %}
<div class="col-12 col-lg-6"> <div class="col-12 col-lg-6">
<div class="card" style=""> <div class="card" style="">
<div class="card-body"> <div class="card-body">
<h5 class="card-title"> <h5 class="card-title">
<a href="{{ url_for('dashboard.domain_detail', custom_domain_id=subdomain.id) }}"> <a href="{{ url_for('dashboard.domain_detail', custom_domain_id=subdomain.id) }}">{{ subdomain.domain }}</a>
{{ subdomain.domain }}
</a>
</h5> </h5>
<h6 class="card-subtitle mb-4 text-muted"> <h6 class="card-subtitle mb-4 text-muted">
Created {{ subdomain.created_at | dt }} <br> Created {{ subdomain.created_at | dt }}
<br />
<span class="font-weight-bold">{{ subdomain.nb_alias() }}</span> aliases. <span class="font-weight-bold">{{ subdomain.nb_alias() }}</span> aliases.
<br> <br />
</h6> </h6>
<a href="{{ url_for('dashboard.domain_detail', custom_domain_id=subdomain.id) }}"
<a href="{{ url_for('dashboard.domain_detail', custom_domain_id=subdomain.id) }}" class="mt-3"> class="mt-3">Details ➡</a>
Details ➡
</a>
</div> </div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
<div class="row {% if current_user.subdomain_quota <= 0 %} disabled-content{% endif %}"
<div class="row {% if current_user.subdomain_quota <= 0 %} disabled-content {% endif %}" id="new-subdomain"> id="new-subdomain">
<div class="col"> <div class="col">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h2 class="h4 mb-1">New Subdomain</h2> <h2 class="h4 mb-1">New Subdomain</h2>
<form method="post" class="mt-2" data-parsley-validate> <form method="post" class="mt-2" data-parsley-validate>
<input type="hidden" name="form-name" value="create"> <input type="hidden" name="form-name" value="create">
<div class="form-group"> <div class="form-group">
<label>Subdomain</label> <label>Subdomain</label>
<input name="subdomain" <input name="subdomain"
@ -80,31 +80,31 @@
data-parsley-pattern="[0-9a-z-]{3,}" data-parsley-pattern="[0-9a-z-]{3,}"
data-parsley-trigger="change" data-parsley-trigger="change"
data-parsley-error-message="At least 3 characters. Only lowercase letters, numbers and dashes (-) are supported." data-parsley-error-message="At least 3 characters. Only lowercase letters, numbers and dashes (-) are supported."
class="form-control" minlength="3" maxlength="32"> class="form-control"
minlength="3"
maxlength="32">
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Root domain</label> <label>Root domain</label>
<select name="domain" v-model="domain" class="form-control"> <select name="domain" v-model="domain" class="form-control">
{% for sl_domain in sl_domains %} {% for sl_domain in sl_domains %}
<option value="{{ sl_domain.domain }}"> <option value="{{ sl_domain.domain }}">
{{ sl_domain.domain }} {{ sl_domain.domain }}
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<button class="btn btn-primary">Create</button> <button class="btn btn-primary">Create</button>
</form> </form>
<div v-if="toShow" class="text-info mt-2"> <div v-if="toShow" class="text-info mt-2">
You are about to create <b>[[ subdomain]].[[domain]]</b> subdomain. You are about to create <b>[[ subdomain]].[[domain]]</b> subdomain.
</div> </div>
<div class="alert alert-info mt-3" style="font-size: 12px"> <div class="alert alert-info mt-3" style="font-size: 12px">
Deleting a subdomain will <b>not</b> restore the subdomain quota Deleting a subdomain will <b>not</b> restore the subdomain quota
so please make sure so please make sure
to choose the subdomain you want to keep. <br> to choose the subdomain you want to keep.
<br />
Currently you can create up to <b style="font-size: 1.5em">{{ current_user.subdomain_quota }}</b> Currently you can create up to <b style="font-size: 1.5em">{{ current_user.subdomain_quota }}</b>
subdomains. subdomains.
</div> </div>
@ -112,14 +112,11 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
new Vue({ new Vue({
el: '#new-subdomain', el: '#new-subdomain',

View File

@ -1,55 +1,63 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = 'dashboard' %} {% set active_page = 'dashboard' %}
{% block title %}Support{% endblock %}
{% block title %}
Support
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="col pb-3"> <div class="col pb-3">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="card-title mb-3">Report a problem</div> <div class="card-title mb-3">Report a problem</div>
<div class="alert alert-info"> <div class="alert alert-info">
If an email cannot be delivered to your mailbox, please check <a href="/dashboard/notifications">your If an email cannot be delivered to your mailbox, please check
notifications</a> for error messages. <br> <a href="{{ url_for('dashboard.notifications_route') }}">
your
notifications
</a>
for error messages.
<br />
For generic questions, i.e. not related to your account, we recommend to post the question on For generic questions, i.e. not related to your account, we recommend to post the question on
our <a href="https://www.reddit.com/r/Simplelogin/">Reddit</a> where our community can help answer the question our
<a href="https://www.reddit.com/r/Simplelogin/">Reddit</a>
where our community can help answer the question
and other people with the same question can find the answer there. and other people with the same question can find the answer there.
</div> </div>
<div class="alert alert-warning"> <div class="alert alert-warning">
A support ticket will be created in Zendesk. Please do not include any sensitive information in the ticket. A support ticket will be created in Zendesk. Please do not include any sensitive information in the ticket.
</div> </div>
<form id="supportZendeskForm" method="post" enctype="multipart/form-data"> <form id="supportZendeskForm" method="post" enctype="multipart/form-data">
<div class="mt-4 mb-5"> <div class="mt-4 mb-5">
<label for="issueDescription" class="form-label font-weight-bold">What happened?</label> <label for="issueDescription" class="form-label font-weight-bold">What happened?</label>
<textarea class="form-control" required name="ticket_content" id="issueDescription" rows="3" <textarea class="form-control" required name="ticket_content" id="issueDescription" rows="3" placeholder="Please provide as much information as possible. For example which alias(es), mailbox(es) ar affected, if this is a persistent issue...">{{- ticket_content or '' -}}</textarea>
placeholder="Please provide as much information as possible. For example which alias(es), mailbox(es) ar affected, if this is a persistent issue...">{{- ticket_content or '' -}}</textarea>
</div>
<div class="mt-5 font-weight-bold">
Attach files to support request
</div> </div>
<div class="mt-5 font-weight-bold">Attach files to support request</div>
<div class="text-muted">Only images, text and emails are accepted</div> <div class="text-muted">Only images, text and emails are accepted</div>
<div class="custom-file"> <div class="custom-file">
<input type="file" class="custom-file-input" name="ticket_files" id="ticketFileGroup" multiple="multiple"> <input type="file"
class="custom-file-input"
name="ticket_files"
id="ticketFileGroup"
multiple="multiple">
<label class="custom-file-label" for="ticketFileGroup">Choose file</label> <label class="custom-file-label" for="ticketFileGroup">Choose file</label>
</div> </div>
<div class="mt-5 font-weight-bold"> <div class="mt-5 font-weight-bold">Where can we reach you?</div>
Where can we reach you?
</div>
<div class="text-muted"> <div class="text-muted">
Conversations related to this ticket will be sent to this address. Feel free to use an alias here. Conversations related to this ticket will be sent to this address. Feel free to use an alias here.
</div> </div>
<div class="input-group mb-3" id="alias-group"> <div class="input-group mb-3" id="alias-group">
<input type="text" required class="form-control" placeholder="Email" name="ticket_email" <input type="text"
v-model='ticket_email' aria-label="Email to send responses to" aria-describedby="button-addon2"> required
class="form-control"
placeholder="Email"
name="ticket_email"
v-model='ticket_email'
aria-label="Email to send responses to"
aria-describedby="button-addon2">
<div class="input-group-append"> <div class="input-group-append">
<button class="btn btn-outline-primary" type="button" @click="generateRandomAlias" id="button-addon2"> <button class="btn btn-outline-primary"
type="button"
@click="generateRandomAlias"
id="button-addon2">
Generate a random alias Generate a random alias
</button> </button>
</div> </div>
@ -58,15 +66,12 @@
<button class="btn btn-primary">Create support ticket</button> <button class="btn btn-primary">Create support ticket</button>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
new Vue({ new Vue({
el: '#alias-group', el: '#alias-group',
@ -95,6 +100,5 @@
} }
$(this).next('.custom-file-label').html(files.join(', ')); $(this).next('.custom-file-label').html(files.join(', '));
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,30 +1,22 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "dashboard" %} {% set active_page = "dashboard" %}
{% block title %}Block an alias{% endblock %}
{% block title %}
Block an alias
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3"> <h1 class="h3">Block alias</h1>
Block alias
</h1>
<p> <p>
You are about to block the alias <a href="mailto:{{ alias }}">{{ alias }}</a> You are about to block the alias
<a href="mailto:{{ alias }}">{{ alias }}</a>
</p> </p>
<p> <p>
After this, you will stop receiving all emails sent to this alias, please confirm. After this, you will stop receiving all emails sent to this alias, please confirm.
</p> </p>
<form method="post"> <form method="post">
<button class="btn btn-warning">Confirm</button> <button class="btn btn-warning">Confirm</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,18 +1,16 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<div class="flex-fill"> <div class="flex-fill">
{% include "header.html" %} {% include "header.html" %}
<div class="my-2 my-md-2"> <div class="my-2 my-md-2">
<div class="container pt-1" style="min-height: 800px"> <div class="container pt-1" style="min-height: 800px">
{% block default_content %} {% block default_content %}{% endblock %}
{% endblock %}
</div> </div>
</div> </div>
</div> </div>
{% include "footer.html" %} {% include "footer.html" %}
{% endblock %} {% endblock %}

View File

@ -1,13 +1,14 @@
{% extends 'developer/client_details/base.html' %} {% extends "developer/client_details/base.html" %}
{% set client_details_page = "advanced" %} {% set client_details_page = "advanced" %}
{% block client_details_content %} {% block client_details_content %}
<h1 class="h2">Danger Zone</h1> <h1 class="h2">Danger Zone</h1>
<form method="post"> <form method="post">
{{ form.csrf_token }} {{ form.csrf_token }}
<div class="alert alert-danger">When your website is deleted, users can no longer log into it. This operation is not reversible!</div> <div class="alert alert-danger">
When your website is deleted, users can no longer log into it. This operation is not reversible!
</div>
<button type="submit" class="btn btn-danger">Delete "{{ client.name }}"</button> <button type="submit" class="btn btn-danger">Delete "{{ client.name }}"</button>
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,64 +1,50 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "developer" %} {% set active_page = "developer" %}
{% block title %}Developer - App {{ client.name }}{% endblock %}
{% block title %}
Developer - App {{ client.name }}
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col-lg-3 order-lg-1 mb-4"> <div class="col-lg-3 order-lg-1 mb-4">
<div class="list-group list-group-transparent mb-0"> <div class="list-group list-group-transparent mb-0">
<a href="{{ url_for('developer.client_detail', client_id=client.id) }}" <a href="{{ url_for('developer.client_detail', client_id=client.id) }}"
class="list-group-item list-group-item-action {{ 'active' if client_details_page == 'basic_info' }}"> class="list-group-item list-group-item-action {{ 'active' if client_details_page == 'basic_info' }}">
<span class="icon mr-3"><i class="fe fe-flag"></i></span>Info <span class="icon mr-3"><i class="fe fe-flag"></i></span>Info
</a> </a>
<a href="{{ url_for('developer.client_detail_oauth_setting', client_id=client.id) }}" <a href="{{ url_for('developer.client_detail_oauth_setting', client_id=client.id) }}"
class="list-group-item list-group-item-action {{ 'active' if client_details_page == 'oauth_setting' }}"> class="list-group-item list-group-item-action {{ 'active' if client_details_page == 'oauth_setting' }}">
<span class="icon mr-3"><i class="fe fe-settings"></i></span>OAuth Settings <span class="icon mr-3"><i class="fe fe-settings"></i></span>OAuth Settings
</a> </a>
<a href="{{ url_for('developer.client_detail_oauth_endpoint', client_id=client.id) }}" <a href="{{ url_for('developer.client_detail_oauth_endpoint', client_id=client.id) }}"
class="list-group-item list-group-item-action {{ 'active' if client_details_page == 'oauth_endpoint' }}"> class="list-group-item list-group-item-action {{ 'active' if client_details_page == 'oauth_endpoint' }}">
<span class="icon mr-3"><i class="fe fe-shield"></i></span>OAuth Endpoints <span class="icon mr-3"><i class="fe fe-shield"></i></span>OAuth Endpoints
</a> </a>
{% if current_user.referrals|count > 0 %} {% if current_user.referrals|count > 0 %}
<a href="{{ url_for('developer.client_detail_referral', client_id=client.id) }}"
class="list-group-item list-group-item-action {{ 'active' if client_details_page == 'referral' }}">
<span class="icon mr-3"><i class="fe fe-share"></i></span>Referral
</a>
{% endif %}
<a href="{{ url_for('developer.client_detail_referral', client_id=client.id) }}"
class="list-group-item list-group-item-action {{ 'active' if client_details_page == 'referral' }}">
<span class="icon mr-3"><i class="fe fe-share"></i></span>Referral
</a>
{% endif %}
<a href="{{ url_for('developer.client_detail_advanced', client_id=client.id) }}" <a href="{{ url_for('developer.client_detail_advanced', client_id=client.id) }}"
class="list-group-item list-group-item-action {{ 'active' if client_details_page == 'advanced' }}"> class="list-group-item list-group-item-action {{ 'active' if client_details_page == 'advanced' }}">
<span class="icon mr-3"><i class="fe fe-alert-octagon"></i></span>Danger <span class="icon mr-3"><i class="fe fe-alert-octagon"></i></span>Danger
</a> </a>
</div> </div>
<a href="https://docs.simplelogin.io"
<a href="https://docs.simplelogin.io" target="_blank" target="_blank"
class="btn btn-block btn-secondary mt-4"> class="btn btn-block btn-secondary mt-4">
Documentation <i class="fe fe-external-link"></i> Documentation <i class="fe fe-external-link"></i>
</a> </a>
</div> </div>
<div class="col-lg-9"> <div class="col-lg-9">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<div class="text-wrap p-lg-6"> <div class="text-wrap p-lg-6">
{% block client_details_content %} {% block client_details_content %}{% endblock %}
{% endblock %}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,83 +1,78 @@
{% extends 'developer/client_details/base.html' %} {% extends "developer/client_details/base.html" %}
{% set client_details_page = "basic_info" %} {% set client_details_page = "basic_info" %}
{% block client_details_content %} {% block client_details_content %}
{% if is_new %} {% if is_new %}
<div class="alert alert-warning alert-dismissible fade show mb-4" role="alert">
<div class="alert alert-warning alert-dismissible fade show mb-4"
role="alert">
<h4 class="alert-heading">Well done!</h4> <h4 class="alert-heading">Well done!</h4>
<p> <p>
Please head to our Please head to our
<a href="http://docs.simplelogin.io" target="_blank" rel="noopener"> <a href="https://docs.simplelogin.io" target="_blank" rel="noopener">
documentation <i class="fe fe-external-link"></i></a> documentation <i class="fe fe-external-link"></i>
</a>
to see how to add SIWSL into your app. to see how to add SIWSL into your app.
</p> </p>
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> <button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true"></span> <span aria-hidden="true"></span>
</button> </button>
</div> </div>
{% endif %} {% endif %}
<form method="post"
<form method="post" enctype="multipart/form-data" enctype="multipart/form-data"
action="{{ url_for('developer.client_detail', client_id=client.id, action="edit") }}"> action="{{ url_for('developer.client_detail', client_id=client.id, action="edit") }}">
{{ form.csrf_token }} {{ form.csrf_token }}
<h1 class="h2">Info</h1> <h1 class="h2">Info</h1>
<div class="form-group"> <div class="form-group">
<label class="form-label">Name</label> <label class="form-label">Name</label>
{{ form.name(class="form-control", value=client.name) }} {{ form.name(class="form-control", value=client.name) }}
{{ render_field_errors(form.name) }} {{ render_field_errors(form.name) }}
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Url</label> <label class="form-label">Url</label>
{{ form.url(class="form-control", value=client.home_url) }} {{ form.url(class="form-control", value=client.home_url) }}
{{ render_field_errors(form.url) }} {{ render_field_errors(form.url) }}
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Icon <label class="form-label">
Icon
<span class="text-muted small-text">The icon is displayed on the authorization page</span> <span class="text-muted small-text">The icon is displayed on the authorization page</span>
</label> </label>
{{ form.icon(class="form-control-file") }} {{ form.icon(class="form-control-file") }}
{{ render_field_errors(form.icon) }} {{ render_field_errors(form.icon) }}
{% if client.icon_id %}<img src="{{ client.icon.get_url() }}" class="client-icon">{% endif %}
{% if client.icon_id %}
<img src="{{ client.icon.get_url() }}" class="client-icon">
{% endif %}
</div> </div>
<button type="submit" class="mt-2 btn btn-primary">Update</button> <button type="submit" class="mt-2 btn btn-primary">Update</button>
</form> </form>
{# <hr />#}
{# <hr>#} {# <h3>Submit for approval</h3>#}
{# <h3>Submit for approval</h3>#} {##}
{##} {# <div class="alert alert-info">#}
{# <div class="alert alert-info">#} {# Approval is only needed when you deploy the <b>Sign in with SimpleLogin</b> integration#}
{# Approval is only needed when you deploy the <b>Sign in with SimpleLogin</b> integration#} {# in production. <br />#}
{# in production. <br>#} {# For local/testing/staging environment, you don't have to submit your app/website for approval. <br />#}
{# For local/testing/staging environment, you don't have to submit your app/website for approval. <br>#} {# </div>#}
{# </div>#} {##}
{##} {# <form method="post" enctype="multipart/form-data"#}
{# <form method="post" enctype="multipart/form-data"#} {# action="{{ url_for('developer.client_detail', client_id=client.id, action="submit") }}">#}
{# action="{{ url_for('developer.client_detail', client_id=client.id, action="submit") }}">#} {# {{ approval_form.csrf_token }}#}
{# {{ approval_form.csrf_token }}#} {##}
{##} {# <div class="form-group">#}
{# <div class="form-group">#} {# <label class="form-label">Tell us about your app</label>#}
{# <label class="form-label">Tell us about your app</label>#} {# {{ approval_form.description(#}
{# {{ approval_form.description(#} {# class="form-control", rows="10",#}
{# class="form-control", rows="10",#} {# placeholder="This information is used for approving your application. Please give us as much info as you can, for example where you plan to use SimpleLogin, for which community, etc."#}
{# placeholder="This information is used for approving your application. Please give us as much info as you can, for example where you plan to use SimpleLogin, for which community, etc."#} {# ) }}#}
{# ) }}#} {# {{ render_field_errors(approval_form.description) }}#}
{# {{ render_field_errors(approval_form.description) }}#} {# </div>#}
{# </div>#} {##}
{##} {# <div class="alert alert-warning">#}
{# <div class="alert alert-warning">#} {# Don't make this frequent mistake: make sure to add your production URL to the <b>Authorized Redirect URIs</b> on#}
{# Don't make this frequent mistake: make sure to add your production URL to the <b>Authorized Redirect URIs</b> on#} {# <a href="{{ url_for('developer.client_detail_oauth_setting', client_id=client.id) }}">OAuth Settings</a>.#}
{# <a href="{{ url_for('developer.client_detail_oauth_setting', client_id=client.id) }}">OAuth Settings</a>.#} {# </div>#}
{# </div>#} {##}
{##} {# <button type="submit" class="btn btn-success">Submit</button>#}
{# <button type="submit" class="btn btn-success">Submit</button>#} {# </form>#}
{# </form>#} {% endblock %}
{% endblock %}

View File

@ -1,68 +1,71 @@
{% extends 'developer/client_details/base.html' %} {% extends "developer/client_details/base.html" %}
{% set client_details_page = "oauth_endpoint" %} {% set client_details_page = "oauth_endpoint" %}
{% block client_details_content %} {% block client_details_content %}
<h1 class="h2">OAuth2 endpoints</h1>
<h1 class="h2">OAuth2 endpoints</h1>
<div class="form-group"> <div class="form-group">
<label class="form-label">OpenID Connect Discovery Document</label> <label class="form-label">OpenID Connect Discovery Document</label>
<div class="input-group mt-2"> <div class="input-group mt-2">
<input type="text" disabled value="{{ URL + "/.well-known/openid-configuration" }}" class="form-control"> <input type="text"
disabled
value="{{ URL + "/.well-known/openid-configuration" }}"
class="form-control">
<span class="input-group-append"> <span class="input-group-append">
<button <button data-clipboard-text="{{ URL + "/.well-known/openid-configuration" }}"
data-clipboard-text="{{ URL + "/.well-known/openid-configuration" }}" class="clipboard btn btn-primary"
class="clipboard btn btn-primary" type="button"> type="button">
<i class="fe fe-clipboard"></i> <i class="fe fe-clipboard"></i>
</button> </button>
</span> </span>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Authorization endpoint</label> <label class="form-label">Authorization endpoint</label>
<div class="input-group mt-2"> <div class="input-group mt-2">
<input type="text" disabled value="{{ URL + "/oauth2/authorize" }}" class="form-control"> <input type="text"
disabled
value="{{ URL + "/oauth2/authorize" }}"
class="form-control">
<span class="input-group-append"> <span class="input-group-append">
<button <button data-clipboard-text="{{ URL + "/oauth2/authorize" }}"
data-clipboard-text="{{ URL + "/oauth2/authorize" }}" class="clipboard btn btn-primary"
class="clipboard btn btn-primary" type="button"> type="button">
<i class="fe fe-clipboard"></i> <i class="fe fe-clipboard"></i>
</button> </button>
</span> </span>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Token endpoint</label> <label class="form-label">Token endpoint</label>
<div class="input-group mt-2"> <div class="input-group mt-2">
<input type="text" disabled value="{{ URL + "/oauth2/token" }}" class="form-control"> <input type="text"
disabled
value="{{ URL + "/oauth2/token" }}"
class="form-control">
<span class="input-group-append"> <span class="input-group-append">
<button <button data-clipboard-text="{{ URL + "/oauth2/token" }}"
data-clipboard-text="{{ URL + "/oauth2/token" }}" class="clipboard btn btn-primary"
class="clipboard btn btn-primary" type="button"> type="button">
<i class="fe fe-clipboard"></i> <i class="fe fe-clipboard"></i>
</button> </button>
</span> </span>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">UserInfo endpoint</label> <label class="form-label">UserInfo endpoint</label>
<div class="input-group mt-2"> <div class="input-group mt-2">
<input type="text" disabled value="{{ URL + "/oauth2/userinfo" }}" class="form-control"> <input type="text"
disabled
value="{{ URL + "/oauth2/userinfo" }}"
class="form-control">
<span class="input-group-append"> <span class="input-group-append">
<button <button data-clipboard-text="{{ URL + "/oauth2/userinfo" }}"
data-clipboard-text="{{ URL + "/oauth2/userinfo" }}" class="clipboard btn btn-primary"
class="clipboard btn btn-primary" type="button"> type="button">
<i class="fe fe-clipboard"></i> <i class="fe fe-clipboard"></i>
</button> </button>
</span> </span>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,54 +1,59 @@
{% extends 'developer/client_details/base.html' %} {% extends "developer/client_details/base.html" %}
{% set client_details_page = "oauth_setting" %} {% set client_details_page = "oauth_setting" %}
{% block client_details_content %} {% block client_details_content %}
<form method="post"> <form method="post">
{{ form.csrf_token }} {{ form.csrf_token }}
<h1 class="h2">OAuth2 Settings</h1> <h1 class="h2">OAuth2 Settings</h1>
<div class="form-group"> <div class="form-group">
<label class="form-label">AppID / OAuth2 Client ID</label> <label class="form-label">AppID / OAuth2 Client ID</label>
<div class="input-group mt-2"> <div class="input-group mt-2">
<input disabled type="text" value="{{ client.oauth_client_id }}" class="form-control"> <input disabled
type="text"
value="{{ client.oauth_client_id }}"
class="form-control">
<span class="input-group-append"> <span class="input-group-append">
<button <button data-clipboard-text="{{ client.oauth_client_id }}"
data-clipboard-text="{{ client.oauth_client_id }}" class="clipboard btn btn-primary"
class="clipboard btn btn-primary" type="button"> type="button">
<i class="fe fe-clipboard"></i> <i class="fe fe-clipboard"></i>
</button> </button>
</span> </span>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">AppSecret / OAuth2 Client Secret</label> <label class="form-label">AppSecret / OAuth2 Client Secret</label>
<div class="input-group mt-2"> <div class="input-group mt-2">
<input disabled type="password" value="{{ client.oauth_client_secret }}" class="form-control"> <input disabled
type="password"
value="{{ client.oauth_client_secret }}"
class="form-control">
<span class="input-group-append"> <span class="input-group-append">
<button <button data-clipboard-text="{{ client.oauth_client_secret }}"
data-clipboard-text="{{ client.oauth_client_secret }}" class="clipboard btn btn-primary"
class="clipboard btn btn-primary" type="button"> type="button">
<i class="fe fe-clipboard"></i> <i class="fe fe-clipboard"></i>
</button> </button>
</span> </span>
</div> </div>
</div> </div>
<hr />
<hr>
<div class="form-group"> <div class="form-group">
<label class="form-label">Authorized Redirect URIs</label> <label class="form-label">Authorized Redirect URIs</label>
<small class="text-muted"> <small class="text-muted">
By default <b>localhost</b> is whitelisted. <br> By default <b>localhost</b> is whitelisted.
<br />
A <b>redirect_uri</b> must be <b>HTTPS</b> for security reason. A <b>redirect_uri</b> must be <b>HTTPS</b> for security reason.
</small> </small>
{% if not client.redirect_uris %} {% if not client.redirect_uris %}
<div class="alert alert-warning alert-dismissible fade show mb-4" role="alert">
<div class="alert alert-warning alert-dismissible fade show mb-4"
role="alert">
<p> <p>
You haven't added any <a href="https://www.oauth.com/oauth2-servers/redirect-uris/">redirect_uri</a>, You haven't added any
<a href="https://www.oauth.com/oauth2-servers/redirect-uris/">redirect_uri</a>
,
that is the url that will receive the <b>code</b> or <b>access-token</b> in OAuth2 flow. that is the url that will receive the <b>code</b> or <b>access-token</b> in OAuth2 flow.
</p> </p>
<p> <p>
@ -66,49 +71,47 @@
</p> </p>
</div> </div>
{% endif %} {% endif %}
{% for redirect_uri in client.redirect_uris %} {% for redirect_uri in client.redirect_uris %}
<div class="input-group mt-2">
<input type="url" name="uri" class="form-control" value="{{ redirect_uri.uri }}"
required pattern="^https:\/\/.*"
title="redirect_uri must be https">
<div class="input-group mt-2">
<input type="url"
name="uri"
class="form-control"
value="{{ redirect_uri.uri }}"
required
pattern="^https:\/\/.*"
title="redirect_uri must be https">
<span class="input-group-append"> <span class="input-group-append">
<button class="remove-uri btn btn-primary" type="button"> <button class="remove-uri btn btn-primary" type="button">
<i class="fe fe-x"></i> <i class="fe fe-x"></i>
</button> </button>
</span> </span>
</div> </div>
{% endfor %} {% endfor %}
<div id="new-uris"> <div id="new-uris">
<!-- New uri will be put here --> <!-- New uri will be put here -->
</div> </div>
<button type="button" id="create-new-uri" class="mt-2 btn btn-secondary">Add new uri</button> <button type="button" id="create-new-uri" class="mt-2 btn btn-secondary">Add new uri</button>
</div> </div>
<button type="submit" class="btn btn-primary">Update</button> <button type="submit" class="btn btn-primary">Update</button>
</form> </form>
<!-- template for new uri --> <!-- template for new uri -->
<div class="input-group mt-2" id="hidden-uri" style="display: none"> <div class="input-group mt-2" id="hidden-uri" style="display: none">
<input type="url" name="uri" class="form-control" <input type="url"
required pattern="^https:\/\/.*" name="uri"
class="form-control"
required
pattern="^https:\/\/.*"
title="redirect_uri must be https"> title="redirect_uri must be https">
<span class="input-group-append"> <span class="input-group-append">
<button class="remove-uri btn btn-primary" type="button"> <button class="remove-uri btn btn-primary" type="button">
<i class="fe fe-x"></i> <i class="fe fe-x"></i>
</button> </button>
</span> </span>
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
$("#create-new-uri").on("click", function (e) { $("#create-new-uri").on("click", function (e) {
var clone = $("#hidden-uri").clone(true, true); // (true, true) to clone withDataAndEvents, deepWithDataAndEvents var clone = $("#hidden-uri").clone(true, true); // (true, true) to clone withDataAndEvents, deepWithDataAndEvents
@ -124,4 +127,4 @@
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,11 +1,13 @@
{% extends 'developer/client_details/base.html' %} {% extends "developer/client_details/base.html" %}
{% set client_details_page = "referral" %} {% set client_details_page = "referral" %}
{% block client_details_content %} {% block client_details_content %}
<h1 class="h2">Referral</h1> <h1 class="h2">Referral</h1>
<div class=""> <div>
If you are in the <a href="{{ url_for('dashboard.referral_route') }}">referral</a> program, you can attach a If you are in the
<a href="{{ url_for('dashboard.referral_route') }}">referral</a>
program, you can attach a
referral to this website. referral to this website.
Any SimpleLogin sign up thanks to the SIWSL on your website will be counted towards this referral. Any SimpleLogin sign up thanks to the SIWSL on your website will be counted towards this referral.
</div> </div>
@ -14,15 +16,20 @@
<label class="form-label" for="client-select">Referral</label> <label class="form-label" for="client-select">Referral</label>
<select class="form-control" name="referral-id" id="client-select"> <select class="form-control" name="referral-id" id="client-select">
{% for referral in current_user.referrals %} {% for referral in current_user.referrals %}
<option value="{{ referral.id }}" <option value="{{ referral.id }}"
{% if client.referral_id == referral.id %} selected {% endif %}>{{ referral.name }}</option> {% if client.referral_id == referral.id %} selected{% endif %}>
{{ referral.name }}
</option>
{% endfor %} {% endfor %}
{% if client.referral_id is none %} {% if client.referral_id is none %}
<option value="" selected>No referral selected</option>
<option value="" selected>
No referral selected
</option>
{% endif %} {% endif %}
</select> </select>
</div> </div>
<input type="submit" class="btn btn-primary" value="Update"> <input type="submit" class="btn btn-primary" value="Update">
</form> </form>
{% endblock %} {% endblock %}

View File

@ -1,92 +1,91 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "developer" %} {% set active_page = "developer" %}
{% block title %}Sign in with SimpleLogin{% endblock %}
{% block title %}
Sign in with SimpleLogin
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<h1 class="h3">Sign in with SimpleLogin (SIWSL) <h1 class="h3">
<a class="ml-3 text-info" style="font-size: 12px" data-toggle="collapse" href="#howtouse" role="button" Sign in with SimpleLogin (SIWSL)
aria-expanded="false" aria-controls="collapseExample"> <a class="ml-3 text-info"
style="font-size: 12px"
data-toggle="collapse"
href="#howtouse"
role="button"
aria-expanded="false"
aria-controls="collapseExample">
How to use <i class="fe fe-chevrons-down"></i> How to use <i class="fe fe-chevrons-down"></i>
</a> </a>
</h1> </h1>
<div class="alert alert-primary collapse {% if not clients %} show{% endif %}"
<div class="alert alert-primary collapse {% if not clients %} show {% endif %}" id="howtouse" role="alert"> id="howtouse"
role="alert">
If you want to integrate SIWSL into your website, If you want to integrate SIWSL into your website,
this page is for you. this page is for you.
<br><br> <br />
<br />
If you are using a CMS or any system that supports a OpenID Connect plugin, you can just point If you are using a CMS or any system that supports a OpenID Connect plugin, you can just point
it to SimpleLogin OpenID Configuration endpoint 👇 it to SimpleLogin OpenID Configuration endpoint 👇
<div class="input-group mt-2"> <div class="input-group mt-2">
<input type="text" disabled value="{{ URL + "/.well-known/openid-configuration" }}" class="form-control"> <input type="text"
disabled
value="{{ URL + "/.well-known/openid-configuration" }}"
class="form-control">
<span class="input-group-append"> <span class="input-group-append">
<button <button data-clipboard-text="{{ URL + "/.well-known/openid-configuration" }}"
data-clipboard-text="{{ URL + "/.well-known/openid-configuration" }}" class="clipboard btn btn-primary"
class="clipboard btn btn-primary" type="button"> type="button">
<i class="fe fe-clipboard"></i> <i class="fe fe-clipboard"></i>
</button> </button>
</span> </span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<div class="btn-group" role="group" aria-label="Basic example"> <div class="btn-group" role="group" aria-label="Basic example">
<a href="{{ url_for('developer.new_client') }}" class="btn btn-primary"> <a href="{{ url_for('developer.new_client') }}" class="btn btn-primary">New website</a>
New website <a href="https://docs.simplelogin.io"
</a> target="_blank"
<a href="https://docs.simplelogin.io" target="_blank" class="ml-2 btn btn-secondary"> class="ml-2 btn btn-secondary">
Docs <i class="fe fe-external-link"></i> Docs <i class="fe fe-external-link"></i>
</a> </a>
</div> </div>
</div> </div>
</div> </div>
<div class="row row-cards row-deck mt-4"> <div class="row row-cards row-deck mt-4">
{% for client in clients %} {% for client in clients %}
<div class="col-12 col-lg-6"> <div class="col-12 col-lg-6">
<div class="card" style=""> <div class="card" style="">
<div class="card-body"> <div class="card-body">
<h5 class="card-title"> <h5 class="card-title">
<a href="{{ url_for('developer.client_detail', client_id=client.id) }}">{{ client.name }}</a> <a href="{{ url_for('developer.client_detail', client_id=client.id) }}">{{ client.name }}</a>
{% if client.approved %} {% if client.approved %}
<span class="cursor" data-toggle="tooltip" data-original-title="Approved"></span> <span class="cursor" data-toggle="tooltip" data-original-title="Approved"></span>
{% else %} {% else %}
<span class="cursor" data-toggle="tooltip" <span class="cursor"
data-toggle="tooltip"
data-original-title="In Dev mode. Please contact us to publish your app."> data-original-title="In Dev mode. Please contact us to publish your app.">
<a href="{{ url_for('developer.client_detail', client_id=client.id) }}" <a href="{{ url_for('developer.client_detail', client_id=client.id) }}"
class="text-decoration-none">🚫 class="text-decoration-none">🚫</a>
</a> </span>
</span>
{% endif %} {% endif %}
</h5> </h5>
<h6 class="card-subtitle mb-4 text-muted"> <h6 class="card-subtitle mb-4 text-muted">
Created {{ client.created_at | dt }} <br> Created {{ client.created_at | dt }}
<br />
<span class="font-weight-bold">{{ client.nb_user() }}</span> users <span class="font-weight-bold">{{ client.nb_user() }}</span> users
<br> <br />
</h6> </h6>
<a href="{{ url_for('developer.client_detail', client_id=client.id) }}"
<a href="{{ url_for('developer.client_detail', client_id=client.id) }}" class="mt-3"> class="mt-3">Details ➡</a>
Details ➡
</a>
</div> </div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,34 +1,26 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "developer" %} {% set active_page = "developer" %}
{% block title %}Developer - Create new website{% endblock %}
{% block title %}
Developer - Create new website
{% endblock %}
{% block default_content %} {% block default_content %}
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="h3">New website</h1> <h1 class="h3">New website</h1>
<form method="post" enctype="multipart/form-data"> <form method="post" enctype="multipart/form-data">
{{ form.csrf_token }} {{ form.csrf_token }}
<div class="form-group"> <div class="form-group">
<label class="form-label">Name</label> <label class="form-label">Name</label>
{{ form.name(class="form-control", placeholder="My Community Forum") }} {{ form.name(class="form-control", placeholder="My Community Forum") }}
{{ render_field_errors(form.name) }} {{ render_field_errors(form.name) }}
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="form-label">Url</label> <label class="form-label">Url</label>
{{ form.url(class="form-control", type="url", placeholder="https://forum.com") }} {{ form.url(class="form-control", type="url", placeholder="https://forum.com") }}
{{ render_field_errors(form.url) }} {{ render_field_errors(form.url) }}
</div> </div>
<button type="submit" class="btn btn-primary">Create</button> <button type="submit" class="btn btn-primary">Create</button>
</form> </form>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,41 +1,29 @@
{% extends 'default.html' %} {% extends "default.html" %}
{% set active_page = "discover" %} {% set active_page = "discover" %}
{% block title %}Discover{% endblock %}
{% block title %}
Discover
{% endblock %}
{% block default_content %} {% block default_content %}
<h3>Apps</h3> <h3>Apps</h3>
<p class="text-muted"> <p class="text-muted">
App/Website that have implemented <b>Connect with SimpeLogin</b> App/Website that have implemented <b>Connect with SimpeLogin</b>
</p> </p>
<div class="row row-cards row-deck"> <div class="row row-cards row-deck">
{% for client in clients %} {% for client in clients %}
<div class="col-sm-4 col-xl-2"> <div class="col-sm-4 col-xl-2">
<div class="card"> <div class="card">
<a href="{{ client.home_url }}" target="_blank" rel="noopener"> <a href="{{ client.home_url }}" target="_blank" rel="noopener">
<img class="card-img-top" src="{{ client.get_icon_url() }}"> <img class="card-img-top" src="{{ client.get_icon_url() }}">
</a> </a>
<div class="card-body d-flex flex-column"> <div class="card-body d-flex flex-column">
<h4><a href="{{ client.home_url }}"> <h4>
{{ client.name }} <a href="{{ client.home_url }}">{{ client.name }}</a>
</a></h4> </h4>
<div class="text-muted">{{ client.home_url }}</div>
<div class="text-muted">
{{ client.home_url }}
</div>
</div> </div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,26 +1,67 @@
{% macro render_text(text) %} {% macro render_text(text) %}
<p style="font-size: 16px; line-height: 1.625; color: #51545E; margin: .4em 0 1.1875em;">{{ text }}</p> <p style="font-size: 16px;
line-height: 1.625;
color: #51545E;
margin: .4em 0 1.1875em;">
{{ text }}
</p>
{% endmacro %} {% endmacro %}
<!-- To be used instead of render_text, much better! --> <!-- To be used instead of render_text, much better! -->
{% macro text() %} {% macro text() %}
<p style="font-size: 16px; line-height: 1.625; color: #51545E; margin: .4em 0 1.1875em;">{{ caller() }}</p> <p style="font-size: 16px;
line-height: 1.625;
color: #51545E;
margin: .4em 0 1.1875em;">
{{ caller() }}
</p>
{% endmacro %} {% endmacro %}
{% macro render_button(button_text, link) %} {% macro render_button(button_text, link) %}
<!-- Action --> <!-- Action -->
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation" <table class="body-action"
style="width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; text-align: center; margin: 30px auto; padding: 0;"> align="center"
width="100%"
cellpadding="0"
cellspacing="0"
role="presentation"
style="width: 100%;
-premailer-width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
text-align: center;
margin: 30px auto;
padding: 0;">
<tr> <tr>
<td align="center" style="word-break: break-word; font-family: Helvetica, Arial, sans-serif; font-size: 16px;"> <td align="center"
style="word-break: break-word;
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;">
<!-- Border based button <!-- Border based button
https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design --> https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design -->
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation"> <table width="100%"
border="0"
cellspacing="0"
cellpadding="0"
role="presentation">
<tr> <tr>
<td align="center" <td align="center"
style="word-break: break-word; font-family: Helvetica, Arial, sans-serif; font-size: 16px;"> style="word-break: break-word;
<a href="{{ link }}" class="f-fallback button" target="_blank" rel="noopener" font-family: Helvetica, Arial, sans-serif;
style="color: #FFF; border-color: #3869d4; border-style: solid; border-width: 10px 18px; background-color: #3869D4; display: inline-block; text-decoration: none; border-radius: 3px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); -webkit-text-size-adjust: none; box-sizing: border-box;"> font-size: 16px;">
<a href="{{ link }}"
class="f-fallback button"
target="_blank"
rel="noopener"
style="color: #FFF;
border-color: #3869d4;
border-style: solid;
border-width: 10px 18px;
background-color: #3869D4;
display: inline-block;
text-decoration: none;
border-radius: 3px;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
-webkit-text-size-adjust: none;
box-sizing: border-box;">
{{ button_text }} {{ button_text }}
</a> </a>
</td> </td>
@ -30,44 +71,71 @@ https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design -->
</tr> </tr>
</table> </table>
{% endmacro %} {% endmacro %}
{% macro raw_url(link) %} {% macro raw_url(link) %}
<!-- Sub copy --> <!-- Sub copy -->
<table class="body-sub" role="presentation" <table class="body-sub"
style="margin-top: 25px; padding-top: 25px; border-top-width: 1px; border-top-color: #EAEAEC; border-top-style: solid;"> role="presentation"
style="margin-top: 25px;
padding-top: 25px;
border-top-width: 1px;
border-top-color: #EAEAEC;
border-top-style: solid;">
<tr> <tr>
<td style="word-break: break-word; font-family: Helvetica, Arial, sans-serif; font-size: 16px;"> <td style="word-break: break-word;
<p class="f-fallback sub" style="font-size: 13px; line-height: 1.625; color: #51545E; margin: .4em 0 1.1875em;"> font-family: Helvetica, Arial, sans-serif;
If youre having trouble with the button above, copy and paste the URL below into your web browser.</p> font-size: 16px;">
<p class="f-fallback sub" <p class="f-fallback sub"
style="font-size: 13px; line-height: 1.625; color: #51545E; margin: .4em 0 1.1875em;">{{ link }}</p> style="font-size: 13px;
line-height: 1.625;
color: #51545E;
margin: .4em 0 1.1875em;">
If youre having trouble with the button above, copy and paste the URL below into your web browser.
</p>
<p class="f-fallback sub"
style="font-size: 13px;
line-height: 1.625;
color: #51545E;
margin: .4em 0 1.1875em;">
{{ link }}
</p>
</td> </td>
</tr> </tr>
</table> </table>
{% endmacro %} {% endmacro %}
{% macro grey_section(parts) %} {% macro grey_section(parts) %}
<table class="attributes" width="100%" cellpadding="0" cellspacing="0" role="presentation" style="margin: 0 0 21px;"> <table class="attributes"
<tr> width="100%"
<td class="attributes_content" cellpadding="0"
style="word-break: break-word; font-family: Helvetica, Arial, sans-serif; font-size: 16px; background-color: #F4F4F7; padding: 16px;" cellspacing="0"
bgcolor="#F4F4F7"> role="presentation"
<table width="100%" cellpadding="0" cellspacing="0" role="presentation"> style="margin: 0 0 21px;">
{% for part in parts %} <tr>
<tr> <td class="attributes_content"
<td class="attributes_item" style="word-break: break-word;
style="word-break: break-word; font-family: Helvetica, Arial, sans-serif; font-size: 16px; padding: 0;"> font-family: Helvetica, Arial, sans-serif;
<div class="f-fallback"> font-size: 16px;
{{ part }} background-color: #F4F4F7;
<br /> padding: 16px;"
<br /> bgcolor="#F4F4F7">
</div> <table width="100%" cellpadding="0" cellspacing="0" role="presentation">
</td> {% for part in parts %}
</tr>
{% endfor %}
</table> <tr>
</td> <td class="attributes_item"
</tr> style="word-break: break-word;
</table> font-family: Helvetica, Arial, sans-serif;
{% endmacro %} font-size: 16px;
padding: 0;">
<div class="f-fallback">
{{ part }}
<br />
<br />
</div>
</td>
</tr>
{% endfor %}
</table>
</td>
</tr>
</table>
{% endmacro %}

View File

@ -1,13 +1,11 @@
{% from "_emailhelpers.html" import render_text, text, render_button, raw_url, grey_section %} {% from "_emailhelpers.html" import render_text, text, render_button, raw_url, grey_section %}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" /> <meta name="x-apple-disable-message-reformatting" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title></title> <style type="text/css" rel="stylesheet" media="all">
<style type="text/css" rel="stylesheet" media="all">
/* Base ------------------------------ */ /* Base ------------------------------ */
body { body {
width: 100% !important; width: 100% !important;
@ -414,15 +412,15 @@
text-shadow: none !important; text-shadow: none !important;
} }
} }
</style> </style>
<!--[if mso]> <!--[if mso]>
<style type="text/css"> <style type="text/css">
.f-fallback { .f-fallback {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
} }
</style> </style>
<![endif]--> <![endif]-->
<style type="text/css" rel="stylesheet" media="all"> <style type="text/css" rel="stylesheet" media="all">
body { body {
width: 100% !important; width: 100% !important;
height: 100%; height: 100%;
@ -438,59 +436,173 @@
background-color: #F2F4F6; background-color: #F2F4F6;
color: #51545E; color: #51545E;
} }
</style> </style>
</head> </head>
<body style="width: 100% !important; height: 100%; -webkit-text-size-adjust: none; font-family: Helvetica, Arial, sans-serif; background-color: #F2F4F6; color: #51545E; margin: 0;" bgcolor="#F2F4F6"> <body style="width: 100% !important;
<span class="preheader" style="display: none !important; visibility: hidden; mso-hide: all; font-size: 1px; line-height: 1px; max-height: 0; max-width: 0; opacity: 0; overflow: hidden;">{{pre_header}}</span> height: 100%;
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation" style="width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; background-color: #F2F4F6; margin: 0; padding: 0;" bgcolor="#F2F4F6"> -webkit-text-size-adjust: none;
<tr> font-family: Helvetica, Arial, sans-serif;
<td align="center" style="word-break: break-word; font-family: Helvetica, Arial, sans-serif; font-size: 16px;"> background-color: #F2F4F6;
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation" style="width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0; padding: 0;"> color: #51545E;
<tr> margin: 0;"
<td class="email-masthead" style="word-break: break-word; font-family: Helvetica, Arial, sans-serif; font-size: 16px; text-align: center; padding: 25px 0;" align="center"> bgcolor="#F2F4F6">
<a href="{{LANDING_PAGE_URL}}" class="f-fallback email-masthead_name" style="color: #A8AAAF; font-size: 16px; font-weight: bold; text-decoration: none; text-shadow: 0 1px 0 white;"> <span class="preheader"
{% block logo %}<img src="{{URL}}/static/logo.png" style="width: 150px; margin: auto">{% endblock %} style="display: none !important;
</a> visibility: hidden;
</td> mso-hide: all;
</tr> font-size: 1px;
<!-- Email Body --> line-height: 1px;
<tr> max-height: 0;
<td class="email-body" width="750" cellpadding="0" cellspacing="0" style="word-break: break-word; margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif; font-size: 16px; width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0;"> max-width: 0;
<table class="email-body_inner" align="center" width="750" cellpadding="0" cellspacing="0" role="presentation" style="width: 750px; -premailer-width: 750px; -premailer-cellpadding: 0; -premailer-cellspacing: 0; background-color: #FFFFFF; margin: 0 auto; padding: 0;" bgcolor="#FFFFFF"> opacity: 0;
<!-- Body content --> overflow: hidden;">{{ pre_header }}</span>
<tr> <table class="email-wrapper"
<td class="content-cell" style="word-break: break-word; font-family: Helvetica, Arial, sans-serif; font-size: 16px; padding: 30px;"> width="100%"
<div class="f-fallback"> cellpadding="0"
{% block greeting %} {% endblock %} cellspacing="0"
{% block content %} {% endblock %} role="presentation"
style="width: 100%;
<!-- Sub copy --> -premailer-width: 100%;
{% block sub_copy %} {% endblock %} -premailer-cellpadding: 0;
</div> -premailer-cellspacing: 0;
</td> background-color: #F2F4F6;
</tr> margin: 0;
</table> padding: 0;"
</td> bgcolor="#F2F4F6">
</tr> <tr>
<tr> <td align="center"
<td style="word-break: break-word; font-family: Helvetica, Arial, sans-serif; font-size: 16px;"> style="word-break: break-word;
<table class="email-footer" align="center" width="750" cellpadding="0" cellspacing="0" role="presentation" style="width: 750px; -premailer-width: 750px; -premailer-cellpadding: 0; -premailer-cellspacing: 0; text-align: center; margin: 0 auto; padding: 0;"> font-family: Helvetica, Arial, sans-serif;
<tr> font-size: 16px;">
<td class="content-cell" align="center" style="word-break: break-word; font-family: Helvetica, Arial, sans-serif; font-size: 16px; padding: 30px;"> <table class="email-content"
<p class="f-fallback sub align-center" style="font-size: 13px; line-height: 1.625; text-align: center; color: #A8AAAF; margin: .4em 0 1.1875em;" align="center">© {{ YEAR }} SimpleLogin. All rights reserved. width="100%"
<br /> cellpadding="0"
{% block footer %} cellspacing="0"
{% endblock %} role="presentation"
</p> style="width: 100%;
<p class="f-fallback sub align-center" style="font-size: 13px; line-height: 1.625; text-align: center; color: #A8AAAF; margin: .4em 0 1.1875em;" align="center"><a href="https://app.simplelogin.io/dashboard/support">Do you have a question?</a></p> -premailer-width: 100%;
</td> -premailer-cellpadding: 0;
</tr> -premailer-cellspacing: 0;
</table> margin: 0;
</td> padding: 0;">
</tr> <tr>
</table> <td class="email-masthead"
</td> style="word-break: break-word;
</tr> font-family: Helvetica, Arial, sans-serif;
</table> font-size: 16px;
</body> text-align: center;
</html> padding: 25px 0;"
align="center">
<a href="{{ LANDING_PAGE_URL }}"
class="f-fallback email-masthead_name"
style="color: #A8AAAF;
font-size: 16px;
font-weight: bold;
text-decoration: none;
text-shadow: 0 1px 0 white;">
{% block logo %}<img src="{{ URL }}/static/logo.png" style="width: 150px; margin: auto">{% endblock %}
</a>
</td>
</tr>
<!-- Email Body -->
<tr>
<td class="email-body"
width="750"
cellpadding="0"
cellspacing="0"
style="word-break: break-word;
margin: 0;
padding: 0;
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;
width: 100%;
-premailer-width: 100%;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;">
<table class="email-body_inner"
align="center"
width="750"
cellpadding="0"
cellspacing="0"
role="presentation"
style="width: 750px;
-premailer-width: 750px;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
background-color: #FFFFFF;
margin: 0 auto;
padding: 0;"
bgcolor="#FFFFFF">
<!-- Body content -->
<tr>
<td class="content-cell"
style="word-break: break-word;
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;
padding: 30px;">
<div class="f-fallback">
{% block greeting %}{% endblock %}
{% block content %}{% endblock %}
<!-- Sub copy -->
{% block sub_copy %}{% endblock %}
</div>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td style="word-break: break-word;
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;">
<table class="email-footer"
align="center"
width="750"
cellpadding="0"
cellspacing="0"
role="presentation"
style="width: 750px;
-premailer-width: 750px;
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
text-align: center;
margin: 0 auto;
padding: 0;">
<tr>
<td class="content-cell"
align="center"
style="word-break: break-word;
font-family: Helvetica, Arial, sans-serif;
font-size: 16px;
padding: 30px;">
<p class="f-fallback sub align-center"
style="font-size: 13px;
line-height: 1.625;
text-align: center;
color: #A8AAAF;
margin: .4em 0 1.1875em;"
align="center">
© {{ YEAR }} SimpleLogin. All rights reserved.
<br />
{% block footer %}{% endblock %}
</p>
<p class="f-fallback sub align-center"
style="font-size: 13px;
line-height: 1.625;
text-align: center;
color: #A8AAAF;
margin: .4em 0 1.1875em;"
align="center">
<a href="https://app.simplelogin.io/dashboard/support">Do you have a question?</a>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -1,35 +1,29 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{{ render_text("Hi") }} {{ render_text("Hi") }}
{{ render_text("Our most requested feature is finally ready: you can now add several <b>real</b> email addresses into SimpleLogin {{ render_text("Our most requested feature is finally ready: you can now add several <b>real</b> email addresses into SimpleLogin
and choose which one to use when creating aliases!") }} and choose which one to use when creating aliases!") }}
{{ render_text("A real email address is called <b>mailbox</b> in SimpleLogin.") }} {{ render_text("A real email address is called <b>mailbox</b> in SimpleLogin.") }}
{{ render_text('This feature is particularly useful if you have several email addresses, {{ render_text('This feature is particularly useful if you have several email addresses,
maybe for different uses: a Gmail account for social networks & forums, a Prontonmail account for professional emails, etc.') }} maybe for different uses: a Gmail account for social networks & forums, a Prontonmail account for professional emails, etc.') }}
<img src="https://simplelogin.io/blog/mailbox-gmail.png"
<img src="https://simplelogin.io/blog/mailbox-gmail.png" alt="Mailbox Gmail"> alt="Mailbox Gmail">
<img src="https://simplelogin.io/blog/mailbox-protonmail.png" alt="Mailbox Protonmail"> <img src="https://simplelogin.io/blog/mailbox-protonmail.png"
alt="Mailbox Protonmail">
{{ render_text("When creating an alias, you can choose which mailbox that <b>owns</b> this alias, meaning:") }} {{ render_text("When creating an alias, you can choose which mailbox that <b>owns</b> this alias, meaning:") }}
{{ render_text("1. Emails sent to this alias are *forwarded* to the owning mailbox.") }} {{ render_text("1. Emails sent to this alias are *forwarded* to the owning mailbox.") }}
{{ render_text("2. The owning mailbox can *send* or reply emails from this alias.") }} {{ render_text("2. The owning mailbox can *send* or reply emails from this alias.") }}
{{ render_text("You can also change the owning mailbox for an existing alias.") }} {{ render_text("You can also change the owning mailbox for an existing alias.") }}
{{ render_text("The mailbox doesn't have to be your personal email: you can also create aliases for your friend by adding his/her email as a mailbox.") }}
{{ render_text('The mailbox doesn\'t have to be your personal email: you can also create aliases for your friend by adding his/her email as a mailbox.') }} {{ render_text('Thanks,
<br />
{{ render_text('Thanks, <br />SimpleLogin Team.') }} SimpleLogin Team.') }}
{{ render_text('<strong>P.S.</strong> Need immediate help getting started? Just reply to this email, the SimpleLogin support team is always ready to help!.') }} {{ render_text('<strong>P.S.</strong> Need immediate help getting started? Just reply to this email, the SimpleLogin support team is always ready to help!.') }}
{% endblock %} {% endblock %}
{% block footer %} {% block footer %}
This email is sent to {{ user.email }}. Unsubscribe on This email is sent to {{ user.email }}. Unsubscribe on
<a href="{{URL}}/dashboard/setting#notification">Settings</a> <a href="{{ URL }}/dashboard/setting#notification">Settings</a>
{% endblock %} {% endblock %}

View File

@ -1,114 +1,125 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{{ render_text("Hi") }} {{ render_text("Hi") }}
{% call text() %} {% call text() %}
Son from SimpleLogin here. I hope you are doing well and are staying at home in this difficult time. By the way I'm Son from SimpleLogin here. I hope you are doing well and are staying at home in this difficult time. By the way I'm
writing this newsletter from my couch with my cats proofreading the text :). <br> writing this newsletter from my couch with my cats proofreading the text :).
Please find below some of our latest news. <br> <br />
Please find below some of our latest news.
{% endcall %} <br />
{% endcall %}
{% call text() %} {% call text() %}
1) <b>Mobile apps</b> <br><br> 1) <b>Mobile apps</b>
<br />
<img src="https://simplelogin.io/blog/devices.png" style="max-width: 100%"> <br><br> <br />
<img src="https://simplelogin.io/blog/devices.png" style="max-width: 100%">
Now you can quickly create aliases on-the-go with SimpleLogin Android and iOS app, <br />
thanks to our mobile guy Thanh-Nhon! <br> <br />
Now you can quickly create aliases on-the-go with SimpleLogin Android and iOS app,
Download the Android app on thanks to our mobile guy Thanh-Nhon!
<a href="https://play.google.com/store/apps/details?id=io.simplelogin.android">Play Store</a> and the iOS app on <br />
<a href="https://apps.apple.com/app/id1494359858">App Store</a>. <br> Download the Android app on
<a href="https://play.google.com/store/apps/details?id=io.simplelogin.android">Play Store</a>
With the release of the mobile apps, SimpleLogin now covers most major platforms: <br> and the iOS app on
<a href="https://apps.apple.com/app/id1494359858">App Store</a>
- Desktop with SimpleLogin web app or Chrome, Firefox and Safari extension <br> .
- Mobile with Android and iOS app <br> <br />
With the release of the mobile apps, SimpleLogin now covers most major platforms:
The code is of course open-source and available on our <a href="http://github.com/simple-login/">Github</a> <br />
- Desktop with SimpleLogin web app or Chrome, Firefox and Safari extension
{% endcall %} <br />
- Mobile with Android and iOS app
{% call text() %} <br />
2) <b>Dark mode</b> <br><br> The code is of course open-source and available on our
<a href="https://github.com/simple-login/">Github</a>
<img src="https://simplelogin.io/blog/dark-mode.gif" style="width: 100%"> <br><br> {% endcall %}
{% call text() %}
You have asked for it and now the dark mode is finally available, kudos to Dung - our full-stack guy. <br> 2) <b>Dark mode</b>
You can finally enjoy using SimpleLogin in the dark. <br />
{% endcall %} <br />
<img src="https://simplelogin.io/blog/dark-mode.gif" style="width: 100%">
{% call text() %} <br />
3) <b>Alias name, new UI, security page, new policy privacy</b> <br><br> <br />
You have asked for it and now the dark mode is finally available, kudos to Dung - our full-stack guy.
<img src="https://simplelogin.io/blog/new-ui.gif" style="width: 100%"> <br><br> <br />
You can finally enjoy using SimpleLogin in the dark.
You might have noticed that the web UI is now more compact: the web app has undergone a remake {% endcall %}
to make it more responsive for usual actions like enabling/disabling an alias, updating alias note, etc. <br> {% call text() %}
3) <b>Alias name, new UI, security page, new policy privacy</b>
You can set a name for your alias too: this name is used when you send emails or reply from your alias.<br> <br />
<br />
We have also created a new <a href="https://simplelogin.io/security/">security page</a> that goes into the technical <img src="https://simplelogin.io/blog/new-ui.gif" style="width: 100%">
details of SimpleLogin. <br />
Our <a href="https://simplelogin.io/privacy/">privacy page</a> is also rewritten from scratch: nothing changes about <br />
your data protection You might have noticed that the web UI is now more compact: the web app has undergone a remake
but the page is more clear and detailed now. to make it more responsive for usual actions like enabling/disabling an alias, updating alias note, etc.
<br />
{% endcall %} You can set a name for your alias too: this name is used when you send emails or reply from your alias.
<br />
{% call text() %} We have also created a new
4) <b>Facebook, Google, Github login deprecation</b> <br> <a href="https://simplelogin.io/security/">security page</a>
We have decided to deprecate those social login options because of several reasons: <br> that goes into the technical
details of SimpleLogin.
- Privacy: every time you sign in using one of these methods, the respective company knows and Our
we have no information on what they do with this data. <br> <a href="https://simplelogin.io/privacy/">privacy page</a>
- Not fully open-standard compatible: these platforms enjoy their monopolies and is also rewritten from scratch: nothing changes about
don't play well with open standards like OAuth2/OpenID: in fact, implementations on mobile of these social login your data protection
require their SDK that we refuse to add because of privacy concern. <br> but the page is more clear and detailed now.
- Uniform experiences for all users: to have these social login in our iOS app, we need to support "Sign in with {% endcall %}
Apple" that isn't broadly available for Android users. {% call text() %}
Again, another big tech enjoying its monopoly. <br> 4) <b>Facebook, Google, Github login deprecation</b>
<br />
If you happen to use one of these social login options, please create a password for your account on the We have decided to deprecate those social login options because of several reasons:
<a href="{{URL}}/dashboard/setting">Setting page</a> <br> <br />
- Privacy: every time you sign in using one of these methods, the respective company knows and
You can still sign in using these social login until 2020-05-31. After this date, they will be removed. we have no information on what they do with this data.
{% endcall %} <br />
- Not fully open-standard compatible: these platforms enjoy their monopolies and
{% call text() %} don't play well with open standards like OAuth2/OpenID: in fact, implementations on mobile of these social login
5) <b>WebAuthn (Beta)</b> <br> require their SDK that we refuse to add because of privacy concern.
<br />
Thanks to Raymond, a user of SimpleLogin, the WebAuthn is now available in Beta. - Uniform experiences for all users: to have these social login in our iOS app, we need to support "Sign in with
Please reply to this email if you want to try this out. Apple" that isn't broadly available for Android users.
Again, another big tech enjoying its monopoly.
{% endcall %} <br />
If you happen to use one of these social login options, please create a password for your account on the
{% call text() %} <a href="{{ URL }}/dashboard/setting">Setting page</a>
<hr style="margin: 10px;"> <br />
On behalf of the team, I want to say thank you to all users who have helped to improve SimpleLogin code You can still sign in using these social login until 2020-05-31. After this date, they will be removed.
and even contribute important features. {% endcall %}
That means a lot to us as SimpleLogin is after all an open-source project. {% call text() %}
5) <b>WebAuthn (Beta)</b>
{% endcall %} <br />
Thanks to Raymond, a user of SimpleLogin, the WebAuthn is now available in Beta.
{% call text() %} Please reply to this email if you want to try this out.
That's all for today. If you want to follow all our latest features, you can follow our {% endcall %}
<a href="https://twitter.com/simple_login">Twitter</a> or join our {% call text() %}
<a href="https://www.reddit.com/r/Simplelogin/">Reddit</a> <hr style="margin: 10px;">
or subscribe to our <a href="https://feed43.com/simplelogin.xml">RSS feed</a>. <br> On behalf of the team, I want to say thank you to all users who have helped to improve SimpleLogin code
Now back to coding :). and even contribute important features.
{% endcall %} That means a lot to us as SimpleLogin is after all an open-source project.
{% endcall %}
{% call text() %} {% call text() %}
Best, <br> That's all for today. If you want to follow all our latest features, you can follow our
Son. <a href="https://twitter.com/simple_login">Twitter</a>
{% endcall %} or join our
<a href="https://www.reddit.com/r/Simplelogin/">Reddit</a>
or subscribe to our
<a href="https://feed43.com/simplelogin.xml">RSS feed</a>
.
<br />
Now back to coding :).
{% endcall %}
{% call text() %}
Best,
<br />
Son.
{% endcall %}
{% endblock %} {% endblock %}
{% block footer %} {% block footer %}
This email is sent to {{ user.email }}. Unsubscribe on This email is sent to {{ user.email }}. Unsubscribe on
<a href="{{URL}}/dashboard/setting#notification">Settings</a> <a href="{{ URL }}/dashboard/setting#notification">Settings</a>
{% endblock %} {% endblock %}

View File

@ -1,40 +1,33 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{{ render_text("Hi") }} {{ render_text("Hi") }}
{{ render_text("If you happen to use Gmail, Yahoo, Outlook, etc, do you know these services can read your emails?") }} {{ render_text("If you happen to use Gmail, Yahoo, Outlook, etc, do you know these services can read your emails?") }}
{{ render_text("If you want to keep your emails only readable by you, Pretty Good Privacy (PGP) is maybe the solution.") }} {{ render_text("If you want to keep your emails only readable by you, Pretty Good Privacy (PGP) is maybe the solution.") }}
{{ render_text('Highly recommended, open source and free, PGP is unfortunately not widely supported. However with SimpleLogin most recent PGP support, you can now enable PGP on emails sent to your aliases easily.') }} {{ render_text('Highly recommended, open source and free, PGP is unfortunately not widely supported. However with SimpleLogin most recent PGP support, you can now enable PGP on emails sent to your aliases easily.') }}
{{ render_text('Without PGP the emails sent to an alias are forwarded by SimpleLogin as-is to your mailbox, leaving anyone in-between or your email service able to read your emails:') }} {{ render_text('Without PGP the emails sent to an alias are forwarded by SimpleLogin as-is to your mailbox, leaving anyone in-between or your email service able to read your emails:') }}
<img src="https://simplelogin.io/blog/without-pgp.png"
<img src="https://simplelogin.io/blog/without-pgp.png" alt="Without PGP" style="max-width: 100%"> alt="Without PGP"
style="max-width: 100%">
{{ render_text("With PGP enabled, all emails arrived at SimpleLogin are encrypted with your public key before being forwarded to your mailbox:") }} {{ render_text("With PGP enabled, all emails arrived at SimpleLogin are encrypted with your public key before being forwarded to your mailbox:") }}
<img src="https://simplelogin.io/blog/with-pgp.png"
<img src="https://simplelogin.io/blog/with-pgp.png" alt="Without PGP" style="max-width: 100%"> alt="Without PGP"
style="max-width: 100%">
{{ render_text("You can find more info on our announcement post on https://simplelogin.io/blog/introducing-pgp/") }} {{ render_text("You can find more info on our announcement post on https://simplelogin.io/blog/introducing-pgp/") }}
{{ render_text("You can create and manage your PGP keys when adding or editing your mailboxes. Check it out on your mailbox dashboard.") }} {{ render_text("You can create and manage your PGP keys when adding or editing your mailboxes. Check it out on your mailbox dashboard.") }}
{{ render_button("Add your PGP key", URL ~ "/dashboard/mailbox") }} {{ render_button("Add your PGP key", URL ~ "/dashboard/mailbox") }}
{{ render_text("Our next important feature is the coming of an iOS app. If you use iPhone or iPad want to help us testing out the app, please reply to this email so we can add you into the TestFlight program. {{ render_text("Our next important feature is the coming of an iOS app. If you use iPhone or iPad want to help us testing out the app, please reply to this email so we can add you into the TestFlight program.
") }} ") }}
{{ render_text("For Android users, don't worry: the Android version is already in progress. {{ render_text("For Android users, don't worry: the Android version is already in progress.
") }} ") }}
{{ render_text('Thanks,
{{ render_text('Thanks, <br />SimpleLogin Team.') }} <br />
SimpleLogin Team.') }}
{{ render_text('<strong>P.S.</strong> Need immediate help getting started? Just reply to this email, the SimpleLogin support team is always ready to help!.') }} {{ render_text('<strong>P.S.</strong> Need immediate help getting started? Just reply to this email, the SimpleLogin support team is always ready to help!.') }}
{% endblock %} {% endblock %}
{% block footer %} {% block footer %}
This email is sent to {{ user.email }}. Unsubscribe on This email is sent to {{ user.email }}. Unsubscribe on
<a href="{{URL}}/dashboard/setting#notification">Settings</a> <a href="{{ URL }}/dashboard/setting#notification">Settings</a>
{% endblock %} {% endblock %}

View File

@ -1,26 +1,43 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<tr> <tr>
<td align="left" valign="top" style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; padding-left: 6.25%; padding-right: 6.25%; width: 87.5%; font-size: 14px; font-weight: 400; line-height: 160%; <td align="left"
padding-top: 25px; valign="top"
color: #000000; style="border-collapse: collapse;
font-family: sans-serif;" class="paragraph"> border-spacing: 0;
margin: 0;
padding: 0;
padding-left: 6.25%;
padding-right: 6.25%;
width: 87.5%;
font-size: 14px;
font-weight: 400;
line-height: 160%;
padding-top: 25px;
color: #000000;
font-family: sans-serif;"
class="paragraph">
This email is sent to {{ user.email }}. This email is sent to {{ user.email }}.
Unsubscribe on <a href="{{URL}}/dashboard/setting#notification">Settings</a> Unsubscribe on
<hr> <a href="{{ URL }}/dashboard/setting#notification">Settings</a>
<hr />
</td> </td>
</tr> </tr>
{{ render_text("Hi") }} {{ render_text("Hi") }}
{{ render_text("If you use Safari on a MacBook or iMac, you should check out our new Safari extension.") }} {{ render_text("If you use Safari on a MacBook or iMac, you should check out our new Safari extension.") }}
{{ render_text('It can be installed on <a href="https://apps.apple.com/app/id1494051017">App Store</a>. Its code is available on <a href="https://github.com/simple-login/mac-app">GitHub</a>.') }} {{ render_text('It can be installed on
<a href="https://apps.apple.com/app/id1494051017">App Store</a>
{{ render_text('<img src="https://static.simplelogin.io/safari-extension.png" style="max-width: 600px">') }} . Its code is available on
<a href="https://github.com/simple-login/mac-app">GitHub</a>
{{ render_text('See our annoucement post for more information on this feature <a href="https://simplelogin.io/blog/safari-extension/">Introducing Safari extension</a>.') }} .') }}
{{ render_text('
<img src="https://static.simplelogin.io/safari-extension.png"
style="max-width: 600px">
') }}
{{ render_text('See our annoucement post for more information on this feature
<a href="https://simplelogin.io/blog/safari-extension/">Introducing Safari extension</a>
.') }}
{{ render_text("As usual, let me know if you have any question by replying to this email.") }} {{ render_text("As usual, let me know if you have any question by replying to this email.") }}
{% endblock %} {% endblock %}

View File

@ -1,34 +1,32 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
Download SimpleLogin browser extensions and mobile apps to create aliases on-the-fly.
</h1>
{% endcall %}
{% call text() %} {% call text() %}
If you want to quickly create aliases <b>without</b> going to SimpleLogin website, you can do that with SimpleLogin <h1>Download SimpleLogin browser extensions and mobile apps to create aliases on-the-fly.</h1>
<a href="https://chrome.google.com/webstore/detail/dphilobhebphkdjbpfohgikllaljmgbn">Chrome</a> {% endcall %}
(or other Chromium-based browsers like Brave or Vivaldi), {% call text() %}
<a href="https://addons.mozilla.org/firefox/addon/simplelogin/">Firefox</a> and If you want to quickly create aliases <b>without</b> going to SimpleLogin website, you can do that with SimpleLogin
<a href="https://apps.apple.com/app/id1494051017 <a href="https://chrome.google.com/webstore/detail/dphilobhebphkdjbpfohgikllaljmgbn">Chrome</a>
">Safari</a> extension. (or other Chromium-based browsers like Brave or Vivaldi),
{% endcall %} <a href="https://addons.mozilla.org/firefox/addon/simplelogin/">Firefox</a>
and
{% call text() %} <a href="https://apps.apple.com/app/id1494051017 ">Safari</a>
You can also manage your aliases using SimpleLogin extension.
<a href="https://play.google.com/store/apps/details?id=io.simplelogin.android">Android App</a> {% endcall %}
or <a href="https://apps.apple.com/app/id1494359858">iOS app</a>. {% call text() %}
{% endcall %} You can also manage your aliases using SimpleLogin
<a href="https://play.google.com/store/apps/details?id=io.simplelogin.android">Android App</a>
<img src="https://simplelogin.io/images/everywhere.png" alt="Available Everywhere" or
style="max-width: 100%;"> <a href="https://apps.apple.com/app/id1494359858">iOS app</a>
.
{% endcall %}
<img src="https://simplelogin.io/images/everywhere.png"
alt="Available Everywhere"
style="max-width: 100%;">
{% endblock %} {% endblock %}
{% block footer %} {% block footer %}
This email is sent to {{ to_email }} and is part of our onboarding series. Unsubscribe on This email is sent to {{ to_email }} and is part of our onboarding series. Unsubscribe on
<a href="{{ URL }}/dashboard/setting#notification">Settings</a> <a href="{{ URL }}/dashboard/setting#notification">Settings</a>
{% endblock %} {% endblock %}

View File

@ -1,38 +1,33 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
Add other mailboxes to SimpleLogin.
</h1>
{% endcall %}
{% call text() %} {% call text() %}
If you have several email inboxes, say Gmail and ProtonMail, <h1>Add other mailboxes to SimpleLogin.</h1>
you can add them into SimpleLogin as <b>mailboxes</b>. {% endcall %}
{% endcall %} {% call text() %}
If you have several email inboxes, say Gmail and ProtonMail,
<img src="https://simplelogin.io/images/multiple-mailboxes.png" alt="Multiple Mailboxes" you can add them into SimpleLogin as <b>mailboxes</b>.
style="max-width: 100%; margin: auto; border: 1px solid"> {% endcall %}
<img src="https://simplelogin.io/images/multiple-mailboxes.png"
{% call text() %} alt="Multiple Mailboxes"
When creating an alias, you can choose the mailbox(es) that style="max-width: 100%; margin: auto; border: 1px solid">
<b>owns</b> this alias, meaning: <br> {% call text() %}
1. Emails sent to this alias are forwarded to the owning mailbox(es). <br> When creating an alias, you can choose the mailbox(es) that
2. The owning mailbox(es) can send emails from this alias. <b>owns</b> this alias, meaning:
{% endcall %} <br />
1. Emails sent to this alias are forwarded to the owning mailbox(es).
{% call text() %} <br />
Please note that adding additional mailboxes is only available in the Premium plan. 2. The owning mailbox(es) can send emails from this alias.
{% endcall %} {% endcall %}
{% call text() %}
{{ render_button("Create mailbox", URL ~ "/dashboard/mailbox") }} Please note that adding additional mailboxes is only available in the Premium plan.
{% endcall %}
{{ raw_url(URL ~ "/dashboard/mailbox") }} {{ render_button("Create mailbox", URL ~ "/dashboard/mailbox") }}
{{ raw_url(URL ~ "/dashboard/mailbox") }}
{% endblock %} {% endblock %}
{% block footer %} {% block footer %}
This email is sent to {{ to_email }} and is part of our onboarding series. Unsubscribe on This email is sent to {{ to_email }} and is part of our onboarding series. Unsubscribe on
<a href="{{ URL }}/dashboard/setting#notification">Settings</a> <a href="{{ URL }}/dashboard/setting#notification">Settings</a>
{% endblock %} {% endblock %}

View File

@ -1,35 +1,33 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
Secure your emails with PGP.
</h1>
{% endcall %}
{% call text() %} {% call text() %}
If you use Gmail, Yahoo, Outlook, etc, you might want to use <h1>Secure your emails with PGP.</h1>
<a href="https://en.wikipedia.org/wiki/Pretty_Good_Privacy">PGP</a> (Pretty Good Privacy) {% endcall %}
to make sure your emails can't be read by these email providers. {% call text() %}
{% endcall %} If you use Gmail, Yahoo, Outlook, etc, you might want to use
<a href="https://en.wikipedia.org/wiki/Pretty_Good_Privacy">PGP</a>
{% call text() %} (Pretty Good Privacy)
Without PGP, emails are stored <b>in plaintext</b> leaving your email service able to read your emails. to make sure your emails can't be read by these email providers.
{% endcall %} {% endcall %}
{% call text() %}
<img src="https://simplelogin.io/blog/without-pgp.png" alt="Without PGP" style="max-width: 100%; margin-bottom: 10px"> Without PGP, emails are stored <b>in plaintext</b> leaving your email service able to read your emails.
{% endcall %}
{% call text() %} <img src="https://simplelogin.io/blog/without-pgp.png"
With PGP enabled, SimpleLogin <b>encrypts</b> your emails with your public key before forwarding to your mailbox. alt="Without PGP"
{% endcall %} style="max-width: 100%; margin-bottom: 10px">
{% call text() %}
<img src="https://simplelogin.io/blog/with-pgp.png" alt="Without PGP" style="max-width: 100%; margin-bottom: 20px"> With PGP enabled, SimpleLogin <b>encrypts</b> your emails with your public key before forwarding to your mailbox.
{% endcall %}
{{ render_button("Enable PGP on your mailbox", URL ~ "/dashboard/mailbox/" ~ user.default_mailbox_id) }} <img src="https://simplelogin.io/blog/with-pgp.png"
{{ raw_url(URL ~ "/dashboard/mailbox/" ~ user.default_mailbox_id) }} alt="Without PGP"
style="max-width: 100%; margin-bottom: 20px">
{{ render_button("Enable PGP on your mailbox", URL ~ "/dashboard/mailbox/" ~ user.default_mailbox_id) }}
{{ raw_url(URL ~ "/dashboard/mailbox/" ~ user.default_mailbox_id) }}
{% endblock %} {% endblock %}
{% block footer %} {% block footer %}
This email is sent to {{ to_email }} and is part of our onboarding series. Unsubscribe on This email is sent to {{ to_email }} and is part of our onboarding series. Unsubscribe on
<a href="{{ URL }}/dashboard/setting#notification">Settings</a> <a href="{{ URL }}/dashboard/setting#notification">Settings</a>
{% endblock %} {% endblock %}

View File

@ -1,48 +1,45 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
Send emails from your alias.
</h1>
{% endcall %}
{% call text() %} {% call text() %}
If you want to reply to an email, just hit "Reply" <h1>Send emails from your alias.</h1>
and the response will come from your alias. Your personal email address stays hidden. {% endcall %}
{% call text() %}
{% endcall %} If you want to reply to an email, just hit "Reply"
and the response will come from your alias. Your personal email address stays hidden.
{% call text() %} {% endcall %}
To send an email to a <b>new contact</b>, please follow the steps below. {% call text() %}
You can also watch this <a href="https://youtu.be/GN060XMt6Pc">Youtube video</a> To send an email to a <b>new contact</b>, please follow the steps below.
that quickly walks you through the steps. You can also watch this
{% endcall %} <a href="https://youtu.be/GN060XMt6Pc">Youtube video</a>
that quickly walks you through the steps.
{% call text() %} {% endcall %}
1. Click the <b>Contacts</b> button on the alias you want to send emails from {% call text() %}
<br> 1. Click the <b>Contacts</b> button on the alias you want to send emails from
<img src="https://simplelogin.io/docs/getting-started/send-email/contacts.png" style="max-width: 500px"> <br />
{% endcall %} <img src="https://simplelogin.io/docs/getting-started/send-email/contacts.png"
style="max-width: 500px">
{% call text() %} {% endcall %}
2. Enter your contact email, this will create a <b>reverse-alias</b> for the contact. <br> {% call text() %}
<img src="https://simplelogin.io/docs/getting-started/send-email/new-contact.png" style="max-width: 500px"> 2. Enter your contact email, this will create a <b>reverse-alias</b> for the contact.
{% endcall %} <br />
<img src="https://simplelogin.io/docs/getting-started/send-email/new-contact.png"
{% call text() %} style="max-width: 500px">
3. Send the email to this reverse-alias <b>instead of the contact email</b>. <br> {% endcall %}
<img src="https://simplelogin.io/docs/getting-started/send-email/reverse-alias.png" style="max-width: 500px"> {% call text() %}
{% endcall %} 3. Send the email to this reverse-alias <b>instead of the contact email</b>.
<br />
{% call text() %} <img src="https://simplelogin.io/docs/getting-started/send-email/reverse-alias.png"
And voilà, your contact will receive this email sent from your alias! style="max-width: 500px">
Your real mailbox address will stay hidden. {% endcall %}
{% endcall %} {% call text() %}
And voilà, your contact will receive this email sent from your alias!
Your real mailbox address will stay hidden.
{% endcall %}
{% endblock %} {% endblock %}
{% block footer %} {% block footer %}
This email is sent to {{ to_email }} and is part of our onboarding series. Unsubscribe on This email is sent to {{ to_email }} and is part of our onboarding series. Unsubscribe on
<a href="{{ URL }}/dashboard/setting#notification">Settings</a> <a href="{{ URL }}/dashboard/setting#notification">Settings</a>
{% endblock %} {% endblock %}

View File

@ -1,57 +1,55 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block logo %}<img src="{{ URL }}/static/logo-sl-proton.svg"
{% block logo %}<img src="{{ URL }}/static/logo-sl-proton.svg" style="width: 150px; margin: auto">{% endblock %} style="width: 150px; margin: auto">{% endblock %}
{% block content %} {% block content %}
{% call text() %}
Welcome to SimpleLogin, a service developed by Proton to protect your email address!
{% endcall %}
{% call text() %} {% call text() %}
This is the first email you receive via your <b>first alias</b> {{ to_address }} Welcome to SimpleLogin, a service developed by Proton to protect your email address!
{% endcall %} {% endcall %}
{% call text() %}
{% call text() %} This is the first email you receive via your <b>first alias</b> {{ to_address }}
This alias is automatically created when you use SimpleLogin for the first time. {% endcall %}
Emails sent to it are forwarded to your Proton mailbox. {% call text() %}
If you want to reply to an email, just hit "Reply" and the response will come from your alias. This alias is automatically created when you use SimpleLogin for the first time.
Your personal email address stays hidden. Emails sent to it are forwarded to your Proton mailbox.
{% endcall %} If you want to reply to an email, just hit "Reply" and the response will come from your alias.
Your personal email address stays hidden.
{% call text() %} {% endcall %}
To create new aliases, use the SimpleLogin browser extension (recommended) or web dashboard. {% call text() %}
SimpleLogin is available on To create new aliases, use the SimpleLogin browser extension (recommended) or web dashboard.
<a href="https://chrome.google.com/webstore/detail/dphilobhebphkdjbpfohgikllaljmgbn">Chrome</a>, SimpleLogin is available on
<a href="https://addons.mozilla.org/firefox/addon/simplelogin/">Firefox</a> <a href="https://chrome.google.com/webstore/detail/dphilobhebphkdjbpfohgikllaljmgbn">Chrome</a>
and ,
<a href="https://microsoftedge.microsoft.com/addons/detail/simpleloginreceive-sen/diacfpipniklenphgljfkmhinphjlfff">Edge</a> <a href="https://addons.mozilla.org/firefox/addon/simplelogin/">Firefox</a>
{% endcall %} and
<a href="https://microsoftedge.microsoft.com/addons/detail/simpleloginreceive-sen/diacfpipniklenphgljfkmhinphjlfff">
{% call text() %} Edge
SimpleLogin is also available on </a>
<a href="https://play.google.com/store/apps/details?id=io.simplelogin.android">Android</a> {% endcall %}
and <a href="https://apps.apple.com/app/id1494359858">iOS</a> so you can manage your aliases on the go. {% call text() %}
SimpleLogin is also available on
{% endcall %} <a href="https://play.google.com/store/apps/details?id=io.simplelogin.android">Android</a>
and
{% call text() %} <a href="https://apps.apple.com/app/id1494359858">iOS</a>
Note, if you are a paying Proton Mail user, you automatically receive the premium version of SimpleLogin. so you can manage your aliases on the go.
{% endcall %}
{% endcall %} {% call text() %}
Note, if you are a paying Proton Mail user, you automatically receive the premium version of SimpleLogin.
{% call text() %} {% endcall %}
For any question, feedback or feature request, please join our {% call text() %}
<a href="https://github.com/simple-login/app/discussions">GitHub forum</a>. For any question, feedback or feature request, please join our
You can also join our <a href="https://www.reddit.com/r/Simplelogin/">Reddit</a> or follow our <a href="https://github.com/simple-login/app/discussions">GitHub forum</a>
<a href="https://twitter.com/simple_login">Twitter</a>. .
{% endcall %} You can also join our
<a href="https://www.reddit.com/r/Simplelogin/">Reddit</a>
{% call text() %} or follow our
Best, <br/> <a href="https://twitter.com/simple_login">Twitter</a>
SimpleLogin Team. .
{% endcall %} {% endcall %}
{% call text() %}
Best,
<br />
SimpleLogin Team.
{% endcall %}
{% endblock %} {% endblock %}

View File

@ -1,65 +1,76 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block greeting %} {% block greeting %}
<h1 style="margin-top: 0; color: #333333; font-size: 22px; font-weight: bold; text-align: left;" align="left">
<h1 style="margin-top: 0;
color: #333333;
font-size: 22px;
font-weight: bold;
text-align: left;"
align="left">
Welcome! Welcome!
</h1> </h1>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
{% if alias %} {% if alias %}
{% call text() %}
This is the first email you receive via your <b>first alias</b> <em>{{ alias }}</em>.
{% endcall %}
{% call text() %} {% call text() %}
This alias is automatically created for receiving SimpleLogin news and tips. <br> This is the first email you receive via your <b>first alias</b> <em>{{ alias }}</em>.
In the next coming days, we'll send you 3 emails to help you get the best out of SimpleLogin. <br> {% endcall %}
Please <a href="{{ URL + '/dashboard/setting#notification' }}">disable</a> it if you don't need this. {% call text() %}
{% endcall %} This alias is automatically created for receiving SimpleLogin news and tips.
{% endif %} <br />
In the next coming days, we'll send you 3 emails to help you get the best out of SimpleLogin.
<br />
Please
<a href="{{ URL + '/dashboard/setting#notification' }}">disable</a>
it if you don't need this.
{% endcall %}
{% endif %}
{% call text() %}
If you are using Firefox or a Chromium-browser like Chrome, Edge, Brave, you can
install our
<a href="https://addons.mozilla.org/firefox/addon/simplelogin/">Firefox add-on</a>
or
<a href="https://chrome.google.com/webstore/detail/dphilobhebphkdjbpfohgikllaljmgbn">Chrome extension</a>
to create aliases in one click (like in the below gif 👇).
{% endcall %}
{% call text() %}
<img src="https://simplelogin.io/images/one-click-alias.gif"
style="max-width: 80%; margin: auto; border: 1px solid">
{% endcall %}
{% call text() %}
SimpleLogin is also available on
<a href="https://play.google.com/store/apps/details?id=io.simplelogin.android">Android</a>
and
<a href="https://apps.apple.com/app/id1494359858">iOS</a>
so you can manage your aliases on the go.
{% endcall %}
{% if user.in_trial() and user.trial_end %}
{% call text() %} {% call text() %}
If you are using Firefox or a Chromium-browser like Chrome, Edge, Brave, you can When you signed up, you can use all premium features like
install our <a href="https://addons.mozilla.org/firefox/addon/simplelogin/">Firefox add-on</a> <em>custom domain</em>, <em>alias directory</em>,
or <a href="https://chrome.google.com/webstore/detail/dphilobhebphkdjbpfohgikllaljmgbn">Chrome extension</a> <em>mailbox</em>,
to create aliases in one click (like in the below gif 👇). <em>PGP</em> without any limit during 7 days (the "trial period").
{% endcall %} Everything you create during this period will
continue to work normally even if you don't upgrade.
{% call text() %} <br />
<img src="https://simplelogin.io/images/one-click-alias.gif" {% endcall %}
style="max-width: 80%; margin: auto; border: 1px solid"> {% call text() %}
{% endcall %} Please note that you can't create more than 15 aliases during the trial period.
<br />
{% call text() %} {% endcall %}
SimpleLogin is also available on {% endif %}
<a href="https://play.google.com/store/apps/details?id=io.simplelogin.android">Android</a> and {% call text() %}
<a href="https://apps.apple.com/app/id1494359858">iOS</a> For any question, feedback or feature request, please join our
so you can manage your aliases on the go. <a href="https://github.com/simple-login/app/discussions">GitHub forum</a>
{% endcall %} .
You can also join our
{% if user.in_trial() and user.trial_end %} <a href="https://www.reddit.com/r/Simplelogin/">Reddit</a>
{% call text() %} or follow our
When you signed up, you can use all premium features like <a href="https://twitter.com/simple_login">Twitter</a>
<em>custom domain</em>, <em>alias directory</em>, .
<em>mailbox</em>, {% endcall %}
<em>PGP</em> without any limit during 7 days (the "trial period").
Everything you create during this period will
continue to work normally even if you don't upgrade.
<br>
{% endcall %}
{% call text() %}
Please note that you can't create more than 15 aliases during the trial period. <br>
{% endcall %}
{% endif %}
{% call text() %}
For any question, feedback or feature request, please join our
<a href="https://github.com/simple-login/app/discussions">GitHub forum</a>.
You can also join our <a href="https://www.reddit.com/r/Simplelogin/">Reddit</a> or follow our
<a href="https://twitter.com/simple_login">Twitter</a>.
{% endcall %}
{% endblock %} {% endblock %}

View File

@ -1,15 +1,14 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
Your SimpleLogin account has been deleted successfully.
</h1>
{% endcall %}
{% call text() %} {% call text() %}
Thank you for having used SimpleLogin. <h1>Your SimpleLogin account has been deleted successfully.</h1>
{% endcall %} {% endcall %}
{% call text() %}
{{ render_text('Best, <br />SimpleLogin Team.') }} Thank you for having used SimpleLogin.
{% endcall %}
{{ render_text('Best,
<br />
SimpleLogin Team.') }}
{% endblock %} {% endblock %}

View File

@ -1,10 +1,12 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{{ render_text("Thank you for choosing SimpleLogin.") }} {{ render_text("Thank you for choosing SimpleLogin.") }}
{{ render_text("To get started, please confirm that <b>" + email + "</b> is your email address by clicking on the button below within 1 hour.") }} {{ render_text("To get started, please confirm that <b>" + email + "</b> is your email address by clicking on the button below within 1 hour.") }}
{{ render_button("Verify email", activation_link) }} {{ render_button("Verify email", activation_link) }}
{{ render_text('Thanks, <br />SimpleLogin Team.') }} {{ render_text('Thanks,
<br />
SimpleLogin Team.') }}
{{ raw_url(activation_link) }} {{ raw_url(activation_link) }}
{% endblock %} {% endblock %}

View File

@ -1,21 +1,16 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
{{ alias.email }} has been transferred.
</h1>
{% endcall %}
{% call text() %} {% call text() %}
Your (previously) alias {{ alias.email }} has been received by another user. <h1>{{ alias.email }} has been transferred.</h1>
{% endcall %} {% endcall %}
{% call text() %}
{% call text() %} Your (previously) alias {{ alias.email }} has been received by another user.
Best, <br/> {% endcall %}
SimpleLogin Team. {% call text() %}
{% endcall %} Best,
<br />
SimpleLogin Team.
{% endcall %}
{% endblock %} {% endblock %}

View File

@ -1,25 +1,21 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
{{ alias.email }} is disabled
</h1>
{% endcall %}
{% call text() %} {% call text() %}
There are several emails sent to your alias {{alias.email}} that have been bounced <h1>{{ alias.email }} is disabled</h1>
by your mailbox {{mailbox_email}}. {% endcall %}
{% endcall %} {% call text() %}
There are several emails sent to your alias {{ alias.email }} that have been bounced
{{ render_button("View the refused email", refused_email_url) }} by your mailbox {{ mailbox_email }}.
{% endcall %}
{% call text() %} {{ render_button("View the refused email", refused_email_url) }}
As security measure, we have disabled the alias. {% call text() %}
{% endcall %} As security measure, we have disabled the alias.
{% endcall %}
{{ render_text('Please let us know if you have any question.') }} {{ render_text('Please let us know if you have any question.') }}
{{ render_text('Thanks,
{{ render_text('Thanks, <br />SimpleLogin Team.') }} <br />
{{ raw_url(refused_email_url) }} SimpleLogin Team.') }}
{{ raw_url(refused_email_url) }}
{% endblock %} {% endblock %}

View File

@ -1,30 +1,25 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
Email cannot be sent to {{ contact.email }} from your alias {{ alias.email }}
</h1>
{% endcall %}
{% call text() %} {% call text() %}
This might mean {{ contact.email }} <h1>Email cannot be sent to {{ contact.email }} from your alias {{ alias.email }}</h1>
<ul> {% endcall %}
<li>is not a valid email address, or</li> {% call text() %}
<li>doesn't exist, or</li> This might mean {{ contact.email }}
<li>its mail server refuses your email</li> <ul>
</ul> <li>is not a valid email address, or</li>
{% endcall %} <li>doesn't exist, or</li>
<li>its mail server refuses your email</li>
{{ render_button("View the original email", refused_email_url) }} </ul>
{% endcall %}
{% call text() %} {{ render_button("View the original email", refused_email_url) }}
This email is automatically deleted in 7 days. {% call text() %}
{% endcall %} This email is automatically deleted in 7 days.
{% endcall %}
{% call text() %} {% call text() %}
Best, <br/> Best,
SimpleLogin Team. <br />
{% endcall %} SimpleLogin Team.
{% endcall %}
{% endblock %} {% endblock %}

View File

@ -1,40 +1,41 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
An email sent to your alias {{ alias.email }} from {{ website_email }} was <b>bounced</b> by your mailbox
{{ mailbox_email }}
</h1>
{% endcall %}
{% call text() %} {% call text() %}
This is usually because your mailbox service thinks the email is <b>spam</b>. <h1>
{% endcall %} An email sent to your alias {{ alias.email }} from {{ website_email }} was <b>bounced</b> by your mailbox
{{ mailbox_email }}
{{ render_button("View the bounced email", refused_email_url) }} </h1>
{% endcall %}
{{ render_text('The email is automatically deleted in 7 days.') }} {% call text() %}
This is usually because your mailbox service thinks the email is <b>spam</b>.
{% call text() %} {% endcall %}
Please consider the following options: <br> {{ render_button("View the bounced email", refused_email_url) }}
{{ render_text('The email is automatically deleted in 7 days.') }}
<ol> {% call text() %}
<li>If the email is not spam, you can create a Please consider the following options:
<a href="https://simplelogin.io/docs/getting-started/troubleshooting/">filter</a> <br />
to explicitly allow all emails from SimpleLogin. <br> <ol>
</li> <li>
If the email is not spam, you can create a
<li> <a href="https://simplelogin.io/docs/getting-started/troubleshooting/">filter</a>
If this email is indeed spam, it means your alias {{ alias.email }} is now in the hands of a spammer. to explicitly allow all emails from SimpleLogin.
You can either <a href="{{ disable_alias_link }}">disable the alias</a> <br />
or <a href="{{ block_sender_link }}">block the sender</a> if they send too many spams. </li>
</li> <li>
</ol> If this email is indeed spam, it means your alias {{ alias.email }} is now in the hands of a spammer.
You can either
<br> <a href="{{ disable_alias_link }}">disable the alias</a>
Please note that the alias can be automatically disabled if too many emails sent to it are bounced. or
{% endcall %} <a href="{{ block_sender_link }}">block the sender</a>
if they send too many spams.
{{ render_text('Thanks, <br />SimpleLogin Team.') }} </li>
</ol>
<br />
Please note that the alias can be automatically disabled if too many emails sent to it are bounced.
{% endcall %}
{{ render_text('Thanks,
<br />
SimpleLogin Team.') }}
{% endblock %} {% endblock %}

View File

@ -1,24 +1,20 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
Cannot create {{ alias }} on-the-fly
</h1>
{% endcall %}
{% call text() %} {% call text() %}
An email has been sent to the alias <b>{{ alias }}</b> that would be created automatically as you own the directory <b>{{ directory }}</b> <h1>Cannot create {{ alias }} on-the-fly</h1>
{% endcall %} {% endcall %}
{% call text() %}
{% call text() %} An email has been sent to the alias <b>{{ alias }}</b> that would be created automatically as you own the directory <b>{{ directory }}</b>
As <b>on-the-fly alias creation is disabled</b> on this directory, the alias isn't created. {% endcall %}
{% endcall %} {% call text() %}
As <b>on-the-fly alias creation is disabled</b> on this directory, the alias isn't created.
{% call text() %} {% endcall %}
If you aren't aware of this alias, that probably means someone has discovered about your directory and is abusing it. {% call text() %}
{% endcall %} If you aren't aware of this alias, that probably means someone has discovered about your directory and is abusing it.
{% endcall %}
{{ render_text('Thanks,
{{ render_text('Thanks, <br />SimpleLogin Team.') }} <br />
SimpleLogin Team.') }}
{% endblock %} {% endblock %}

View File

@ -1,17 +1,14 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %} {% call text() %}
<h1> <h1>Cannot create alias {{ alias }} on-the-fly</h1>
Cannot create alias {{ alias }} on-the-fly {% endcall %}
</h1> {{ render_text("An email has been sent to the alias <b>" + alias + "</b> that would be created automatically as you own the directory <b>" + directory + "</b>.") }}
{% endcall %} {{ render_text("However you have reached the alias limit in your current plan, this creation cannot happen.") }}
{{ render_text('Please upgrade to premium plan in order to use this feature.') }}
{{ render_text("An email has been sent to the alias <b>" + alias + "</b> that would be created automatically as you own the directory <b>" + directory + "</b>.") }} {{ render_text('Thanks,
<br />
{{ render_text("However you have reached the alias limit in your current plan, this creation cannot happen.") }} SimpleLogin Team.') }}
{{ render_text('Please upgrade to premium plan in order to use this feature.') }}
{{ render_text('Thanks, <br />SimpleLogin Team.') }}
{% endblock %} {% endblock %}

View File

@ -1,17 +1,14 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %} {% call text() %}
<h1> <h1>Cannot create {{ alias }} on-the-fly</h1>
Cannot create {{ alias }} on-the-fly {% endcall %}
</h1> {{ render_text("An email has been sent to the alias <b>" + alias + "</b> that would be created automatically as you own the domain <b>" + domain + "</b>.") }}
{% endcall %} {{ render_text("However you have reached the alias limit in your current plan, this creation cannot happen.") }}
{{ render_text('Please upgrade to premium plan in order to use this feature.') }}
{{ render_text("An email has been sent to the alias <b>" + alias + "</b> that would be created automatically as you own the domain <b>" + domain + "</b>.") }} {{ render_text('Thanks,
<br />
{{ render_text("However you have reached the alias limit in your current plan, this creation cannot happen.") }} SimpleLogin Team.') }}
{{ render_text('Please upgrade to premium plan in order to use this feature.') }}
{{ render_text('Thanks, <br />SimpleLogin Team.') }}
{% endblock %} {% endblock %}

View File

@ -1,11 +1,13 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{{ render_text("You recently requested to change your email on SimpleLogin to <b>"+ new_email +"</b>.") }} {{ render_text("You recently requested to change your email on SimpleLogin to <b>"+ new_email +"</b>.") }}
{{ render_text("Your current email is " + current_email + ".") }} {{ render_text("Your current email is " + current_email + ".") }}
{{ render_text("Use the button below to confirm within the next 12 hours.") }} {{ render_text("Use the button below to confirm within the next 12 hours.") }}
{{ render_button("Change email", link) }} {{ render_button("Change email", link) }}
{{ render_text('Thanks, <br />SimpleLogin Team.') }} {{ render_text('Thanks,
<br />
SimpleLogin Team.') }}
{{ raw_url(link) }} {{ raw_url(link) }}
{% endblock %} {% endblock %}

View File

@ -1,10 +1,14 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{{ render_text("Hi") }} {{ render_text("Hi") }}
{{ render_text("Thank you for choosing SimpleLogin.") }} {{ render_text("Thank you for choosing SimpleLogin.") }}
{{ render_text("To get started, please activate your account by entering the following code into the application:") }} {{ render_text("To get started, please activate your account by entering the following code into the application:") }}
{{ render_text("<h1>" + code + "</h1>")}} {{ render_text("
{{ render_text('Thanks, <br />SimpleLogin Team.') }} <h1>" + code + "</h1>
")}}
{{ render_text('Thanks,
<br />
SimpleLogin Team.') }}
{% endblock %} {% endblock %}

View File

@ -1,23 +1,20 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
Your subscription has been extended!
</h1>
{% endcall %}
{% call text() %} {% call text() %}
Your payment with cryptocurrency has been successfully processed. <br> <h1>Your subscription has been extended!</h1>
Your subscription has been extended to {% endcall %}
<b>{{ coinbase_subscription.end_at.format("YYYY-MM-DD") }}</b> {% call text() %}
{% endcall %} Your payment with cryptocurrency has been successfully processed.
<br />
Your subscription has been extended to
{% call text() %} <b>{{ coinbase_subscription.end_at.format("YYYY-MM-DD") }}</b>
Thank you a lot for your support! {% endcall %}
{% endcall %} {% call text() %}
Thank you a lot for your support!
{% endcall %}
{{ render_text('Best, <br />SimpleLogin Team.') }} {{ render_text('Best,
<br />
SimpleLogin Team.') }}
{% endblock %} {% endblock %}

View File

@ -1,22 +1,20 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
Your account has been upgraded!
</h1>
{% endcall %}
{% call text() %} {% call text() %}
Your payment with cryptocurrency has been successfully processed. <br> <h1>Your account has been upgraded!</h1>
Your account has been upgraded to the premium plan until {% endcall %}
<b>{{ coinbase_subscription.end_at.format("YYYY-MM-DD") }}</b> {% call text() %}
{% endcall %} Your payment with cryptocurrency has been successfully processed.
<br />
{% call text() %} Your account has been upgraded to the premium plan until
Thank you a lot for your support! <b>{{ coinbase_subscription.end_at.format("YYYY-MM-DD") }}</b>
{% endcall %} {% endcall %}
{% call text() %}
Thank you a lot for your support!
{{ render_text('Best, <br />SimpleLogin Team.') }} {% endcall %}
{{ render_text('Best,
<br />
SimpleLogin Team.') }}
{% endblock %} {% endblock %}

View File

@ -1,18 +1,16 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
Your subscription is ending soon.
</h1>
{% endcall %}
{% call text() %} {% call text() %}
Your subscription ends on <h1>Your subscription is ending soon.</h1>
<b>{{ coinbase_subscription.end_at.format("YYYY-MM-DD") }}</b> {% endcall %}
{% endcall %} {% call text() %}
Your subscription ends on
{{ render_button("Extend your subscription", extend_subscription_url) }} <b>{{ coinbase_subscription.end_at.format("YYYY-MM-DD") }}</b>
{% endcall %}
{{ render_text('Best, <br />SimpleLogin Team.') }} {{ render_button("Extend your subscription", extend_subscription_url) }}
{{ render_text('Best,
<br />
SimpleLogin Team.') }}
{% endblock %} {% endblock %}

View File

@ -1,23 +1,23 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
An email was sent to your alias <b>{{ alias.email }}</b> from its own mailbox
<b>{{ from_addr }}</b>.
</h1>
{% endcall %}
{% call text() %} {% call text() %}
SimpleLogin doesn't send this email <b>back</b> to your mailbox as it would be refused or hidden anyway by your email service. <br> <h1>
So no worries, there's nothing you need to do :). An email was sent to your alias <b>{{ alias.email }}</b> from its own mailbox
{% endcall %} <b>{{ from_addr }}</b>.
</h1>
{{ render_button("View the email", refused_email_url) }} {% endcall %}
{% call text() %}
{% call text() %} SimpleLogin doesn't send this email <b>back</b> to your mailbox as it would be refused or hidden anyway by your email service.
The email is automatically deleted in 7 days. <br />
{% endcall %} So no worries, there's nothing you need to do :).
{% endcall %}
{{ render_text('Thanks, <br />SimpleLogin Team.') }} {{ render_button("View the email", refused_email_url) }}
{% call text() %}
The email is automatically deleted in 7 days.
{% endcall %}
{{ render_text('Thanks,
<br />
SimpleLogin Team.') }}
{% endblock %} {% endblock %}

View File

@ -1,31 +1,24 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
Issues with {{ mailbox.email }}.
</h1>
{% endcall %}
{% call text() %} {% call text() %}
We have detected that your mailbox can't reliably receive emails from SimpleLogin for a consecutive period of time. <br> <h1>Issues with {{ mailbox.email }}.</h1>
The mailbox will be disabled in 5 days if the problem isn't solved by then. {% endcall %}
{% endcall %} {% call text() %}
We have detected that your mailbox can't reliably receive emails from SimpleLogin for a consecutive period of time.
{% call text() %} <br />
Please note that a mailbox can't be a disposable or forwarding email address. The mailbox will be disabled in 5 days if the problem isn't solved by then.
{% endcall %} {% endcall %}
{% call text() %}
{% call text() %} Please note that a mailbox can't be a disposable or forwarding email address.
If you have any question, you can reach out to us by replying to this email. {% endcall %}
{% endcall %} {% call text() %}
If you have any question, you can reach out to us by replying to this email.
{% call text() %} {% endcall %}
Best, <br/> {% call text() %}
SimpleLogin Team. Best,
{% endcall %} <br />
SimpleLogin Team.
{% endcall %}
{% endblock %} {% endblock %}

View File

@ -1,31 +1,24 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %}
<h1>
{{ mailbox.email }} is disabled.
</h1>
{% endcall %}
{% call text() %} {% call text() %}
We have detected that your mailbox can't reliably receive emails from SimpleLogin for a consecutive period of time. <br> <h1>{{ mailbox.email }} is disabled.</h1>
The mailbox has been therefore disabled. {% endcall %}
{% endcall %} {% call text() %}
We have detected that your mailbox can't reliably receive emails from SimpleLogin for a consecutive period of time.
{% call text() %} <br />
Please note that a mailbox can't be a disposable or forwarding email address. The mailbox has been therefore disabled.
{% endcall %} {% endcall %}
{% call text() %}
{% call text() %} Please note that a mailbox can't be a disposable or forwarding email address.
If you have any question, you can reach out to us by replying to this email. {% endcall %}
{% endcall %} {% call text() %}
If you have any question, you can reach out to us by replying to this email.
{% call text() %} {% endcall %}
Best, <br/> {% call text() %}
SimpleLogin Team. Best,
{% endcall %} <br />
SimpleLogin Team.
{% endcall %}
{% endblock %} {% endblock %}

View File

@ -1,46 +1,43 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
{% call text() %} {% call text() %}
<h1> <h1>{{ breached_aliases|count }} of your aliases are found in data breaches.</h1>
{{ breached_aliases|count }} of your aliases are found in data breaches. {% endcall %}
</h1> <ol>
{% endcall %} {%- for alias in breached_aliases[:10] %}
<li>
<ol> {% call text() %}
{%- for alias in breached_aliases[:10] %} <b>{{ alias.email }}</b> was found in {{ alias.hibp_breaches|count }} data breaches.
<li>{% call text() %} <br />
<b>{{ alias.email }}</b> was found in {{ alias.hibp_breaches|count }} data breaches. <br> <ul>
{% set breaches = alias.hibp_breaches|sort(attribute='date', reverse=True) %}
<ul> {%- for breach in breaches[:4] %}
{% set breaches = alias.hibp_breaches|sort(attribute='date', reverse=True) %} <li>
{%- for breach in breaches[:4] %} <b>{{ breach.name }}</b>
<li> {% if breach.date %}({{ breach.date.format('YYYY-MM-DD') }}){% endif %}
<b>{{ breach.name }}</b> {% if breach.date %}({{ breach.date.format('YYYY-MM-DD') }}){% endif %} {{ breach.description }}
{{ breach.description }} </li>
</li> {%- endfor %}
{%- endfor %} </ul>
</ul> {% if breaches|length > 4 %}And {{ breaches|length - 4 }} more data breaches...{% endif %}
{% if breaches|length > 4 %}
And {{ breaches|length - 4 }} more data breaches...
{% endif %}
{% endcall %}</li>
{%- endfor %}
</ol>
{% if breached_aliases|length > 10 %}
{% call text() %}
And {{ breached_aliases|length - 10 }} more aliases...
{% endcall %} {% endcall %}
{% endif %} </li>
{%- endfor %}
</ol>
{% if breached_aliases|length > 10 %}
{% call text() %} {% call text() %}
For more information, check <a href='https://haveibeenpwned.com/'>HaveIBeenPwned.com</a>. And {{ breached_aliases|length - 10 }} more aliases...
{% endcall %} {% endcall %}
{% endif %}
{% call text() %}
{{ render_text('Best, <br />SimpleLogin Team.') }} For more information, check
{% endblock %} <a href='https://haveibeenpwned.com/'>HaveIBeenPwned.com</a>
.
{% endcall %}
{{ render_text('Best,
<br />
SimpleLogin Team.') }}
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More