mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-19 10:59:21 +02:00
Merge pull request #2100 from cisagov/za/2050-waffle-feature-flags
(on getgov-za) Ticket #2054: Add waffle feature flags
This commit is contained in:
commit
d71814faa9
12 changed files with 319 additions and 106 deletions
23
docs/developer/adding-feature-flags.md
Normal file
23
docs/developer/adding-feature-flags.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Adding feature flags
|
||||||
|
Feature flags are booleans (stored in our DB as the `WaffleFlag` object) that programmatically disable/enable "features" (such as DNS hosting) for a specified set of users.
|
||||||
|
|
||||||
|
We use [django-waffle](https://waffle.readthedocs.io/en/stable/) for our feature flags. Waffle makes using flags fairly straight forward.
|
||||||
|
|
||||||
|
## Adding feature flags through django admin
|
||||||
|
1. On the app, navigate to `\admin`.
|
||||||
|
2. Under models, click `Waffle flags`.
|
||||||
|
3. Click `Add waffle flag`.
|
||||||
|
4. Add the model as you would normally. Refer to waffle's documentation [regarding attributes](https://waffle.readthedocs.io/en/stable/types/flag.html#flag-attributes) for more information on them.
|
||||||
|
|
||||||
|
### Enabling the profile_feature flag
|
||||||
|
1. On the app, navigate to `\admin`.
|
||||||
|
2. Under models, click `Waffle flags`.
|
||||||
|
3. Click the `profile_feature` record. This should exist by default, if not - create one with that name.
|
||||||
|
4. (Important) Set the field `Everyone` to `Unknown`. This field overrides all other settings when set to anything else.
|
||||||
|
5. Configure the settings as you see fit.
|
||||||
|
|
||||||
|
## Using feature flags as boolean values
|
||||||
|
Waffle [provides a boolean](https://waffle.readthedocs.io/en/stable/usage/views.html) called `flag_is_active` that you can use as you otherwise would a boolean. This boolean requires a request object and the flag name.
|
||||||
|
|
||||||
|
## Using feature flags to disable/enable views
|
||||||
|
Waffle [provides a decorator](https://waffle.readthedocs.io/en/stable/usage/decorators.html) that you can use to enable/disable views. When disabled, the view will return a 404 if said user tries to navigate to it.
|
|
@ -32,6 +32,7 @@ fred-epplib = {git = "https://github.com/cisagov/epplib.git", ref = "master"}
|
||||||
pyzipper="*"
|
pyzipper="*"
|
||||||
tblib = "*"
|
tblib = "*"
|
||||||
django-admin-multiple-choice-list-filter = "*"
|
django-admin-multiple-choice-list-filter = "*"
|
||||||
|
django-waffle = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
django-debug-toolbar = "*"
|
django-debug-toolbar = "*"
|
||||||
|
|
190
src/Pipfile.lock
generated
190
src/Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "16a0db98015509322cf1d27f06fced5b7635057c4eb98921a9419d63d51925ab"
|
"sha256": "9095c4f98f58a9502444584067a63f329d5a5fc4b49454c4e129bda09552d19d"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {},
|
"requires": {},
|
||||||
|
@ -32,20 +32,20 @@
|
||||||
},
|
},
|
||||||
"boto3": {
|
"boto3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2824e3dd18743ca50e5b10439d20e74647b1416e8a94509cb30beac92d27a18d",
|
"sha256:decf52f8d5d8a1b10c9ff2a0e96ee207ed79e33d2e53fdf0880a5cbef70785e0",
|
||||||
"sha256:b2e5cb5b95efcc881e25a3bc872d7a24e75ff4e76f368138e4baf7b9d6ee3422"
|
"sha256:e836b71d79671270fccac0a4d4c8ec239a6b82ea47c399b64675aa597d0ee63b"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==1.34.90"
|
"version": "==1.34.95"
|
||||||
},
|
},
|
||||||
"botocore": {
|
"botocore": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:113cd4c0cb63e13163ccbc2bb13d551be314ba7f8ba5bfab1c51a19ca01aa133",
|
"sha256:6bd76a2eadb42b91fa3528392e981ad5b4dfdee3968fa5b904278acf6cbf15ff",
|
||||||
"sha256:d48f152498e2c60b43ce25b579d26642346a327b6fb2c632d57219e0a4f63392"
|
"sha256:ead5823e0dd6751ece5498cb979fd9abf190e691c8833bcac6876fd6ca261fa7"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==1.34.90"
|
"version": "==1.34.95"
|
||||||
},
|
},
|
||||||
"cachetools": {
|
"cachetools": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -370,6 +370,15 @@
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==7.3.0"
|
"version": "==7.3.0"
|
||||||
},
|
},
|
||||||
|
"django-waffle": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:5979a2f3dd674ef7086480525b39651fc2045427f6d8e6a614192656d3402c5b",
|
||||||
|
"sha256:e49d7d461d89f3bd8e53f20efe39310acca8f275c9888495e68e195345bf18b1"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"markers": "python_version >= '3.8'",
|
||||||
|
"version": "==4.1.0"
|
||||||
|
},
|
||||||
"django-widget-tweaks": {
|
"django-widget-tweaks": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:1c2180681ebb994e922c754804c7ffebbe1245014777ac47897a81f57cc629c7",
|
"sha256:1c2180681ebb994e922c754804c7ffebbe1245014777ac47897a81f57cc629c7",
|
||||||
|
@ -392,12 +401,12 @@
|
||||||
},
|
},
|
||||||
"faker": {
|
"faker": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:34b947581c2bced340c39b35f89dbfac4f356932cfff8fe893bde854903f0e6e",
|
"sha256:87ef41e24b39a5be66ecd874af86f77eebd26782a2681200e86c5326340a95d3",
|
||||||
"sha256:adb98e771073a06bdc5d2d6710d8af07ac5da64c8dc2ae3b17bb32319e66fd82"
|
"sha256:e23a2b74888885c3d23a9237bacb823041291c03d609a39acb9ebe6c123f3986"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==24.11.0"
|
"version": "==25.0.0"
|
||||||
},
|
},
|
||||||
"fred-epplib": {
|
"fred-epplib": {
|
||||||
"git": "https://github.com/cisagov/epplib.git",
|
"git": "https://github.com/cisagov/epplib.git",
|
||||||
|
@ -588,7 +597,6 @@
|
||||||
"sha256:3d0c3dd24bb4605439bf91068598d00c6370684f8de4a67c2992683f6c309d6b",
|
"sha256:3d0c3dd24bb4605439bf91068598d00c6370684f8de4a67c2992683f6c309d6b",
|
||||||
"sha256:3dbe858ee582cbb2c6294dc85f55b5f19c918c2597855e950f34b660f1a5ede6",
|
"sha256:3dbe858ee582cbb2c6294dc85f55b5f19c918c2597855e950f34b660f1a5ede6",
|
||||||
"sha256:3dc773b2861b37b41a6136e0b72a1a44689a9c4c101e0cddb6b854016acc0aa8",
|
"sha256:3dc773b2861b37b41a6136e0b72a1a44689a9c4c101e0cddb6b854016acc0aa8",
|
||||||
"sha256:3e183c6e3298a2ed5af9d7a356ea823bccaab4ec2349dc9ed83999fd289d14d5",
|
|
||||||
"sha256:3f7765e69bbce0906a7c74d5fe46d2c7a7596147318dbc08e4a2431f3060e306",
|
"sha256:3f7765e69bbce0906a7c74d5fe46d2c7a7596147318dbc08e4a2431f3060e306",
|
||||||
"sha256:417d14450f06d51f363e41cace6488519038f940676ce9664b34ebf5653433a5",
|
"sha256:417d14450f06d51f363e41cace6488519038f940676ce9664b34ebf5653433a5",
|
||||||
"sha256:44f6c7caff88d988db017b9b0e4ab04934f11e3e72d478031efc7edcac6c622f",
|
"sha256:44f6c7caff88d988db017b9b0e4ab04934f11e3e72d478031efc7edcac6c622f",
|
||||||
|
@ -801,12 +809,12 @@
|
||||||
},
|
},
|
||||||
"oic": {
|
"oic": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:385a1f64bb59519df1e23840530921bf416740240f505ea6d161e331d3d39fad",
|
"sha256:b74bd06c7de1ab4f8e798f714062e6a68f68ad9cdbed1f1c30a7fb887602f321",
|
||||||
"sha256:fcbf948a22e4d4df66f6bf57d327933f32a7b539640d9b42883457634360ba78"
|
"sha256:e51705d0c14c97e9ca594374bfb54269a72c9b489e0e979598344c0189bfcb64"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version ~= '3.7'",
|
"markers": "python_version ~= '3.8'",
|
||||||
"version": "==1.6.1"
|
"version": "==1.7.0"
|
||||||
},
|
},
|
||||||
"orderedmultidict": {
|
"orderedmultidict": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1244,49 +1252,49 @@
|
||||||
},
|
},
|
||||||
"black": {
|
"black": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:1bb9ca06e556a09f7f7177bc7cb604e5ed2d2df1e9119e4f7d2f1f7071c32e5d",
|
"sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474",
|
||||||
"sha256:21f9407063ec71c5580b8ad975653c66508d6a9f57bd008bb8691d273705adcd",
|
"sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1",
|
||||||
"sha256:4396ca365a4310beef84d446ca5016f671b10f07abdba3e4e4304218d2c71d33",
|
"sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0",
|
||||||
"sha256:44d99dfdf37a2a00a6f7a8dcbd19edf361d056ee51093b2445de7ca09adac965",
|
"sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8",
|
||||||
"sha256:5cd5b4f76056cecce3e69b0d4c228326d2595f506797f40b9233424e2524c070",
|
"sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96",
|
||||||
"sha256:64578cf99b6b46a6301bc28bdb89f9d6f9b592b1c5837818a177c98525dbe397",
|
"sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1",
|
||||||
"sha256:64e60a7edd71fd542a10a9643bf369bfd2644de95ec71e86790b063aa02ff745",
|
"sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04",
|
||||||
"sha256:652e55bb722ca026299eb74e53880ee2315b181dfdd44dca98e43448620ddec1",
|
"sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021",
|
||||||
"sha256:6644f97a7ef6f401a150cca551a1ff97e03c25d8519ee0bbc9b0058772882665",
|
"sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94",
|
||||||
"sha256:6ad001a9ddd9b8dfd1b434d566be39b1cd502802c8d38bbb1ba612afda2ef436",
|
"sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d",
|
||||||
"sha256:71d998b73c957444fb7c52096c3843875f4b6b47a54972598741fe9a7f737fcb",
|
"sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c",
|
||||||
"sha256:74eb9b5420e26b42c00a3ff470dc0cd144b80a766128b1771d07643165e08d0e",
|
"sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7",
|
||||||
"sha256:75a2d0b4f5eb81f7eebc31f788f9830a6ce10a68c91fbe0fade34fff7a2836e6",
|
"sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c",
|
||||||
"sha256:7852b05d02b5b9a8c893ab95863ef8986e4dda29af80bbbda94d7aee1abf8702",
|
"sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc",
|
||||||
"sha256:7f2966b9b2b3b7104fca9d75b2ee856fe3fdd7ed9e47c753a4bb1a675f2caab8",
|
"sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7",
|
||||||
"sha256:8e5537f456a22cf5cfcb2707803431d2feeb82ab3748ade280d6ccd0b40ed2e8",
|
"sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d",
|
||||||
"sha256:d4e71cdebdc8efeb6deaf5f2deb28325f8614d48426bed118ecc2dcaefb9ebf3",
|
"sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c",
|
||||||
"sha256:dae79397f367ac8d7adb6c779813328f6d690943f64b32983e896bcccd18cbad",
|
"sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741",
|
||||||
"sha256:e3a3a092b8b756c643fe45f4624dbd5a389f770a4ac294cf4d0fce6af86addaf",
|
"sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce",
|
||||||
"sha256:eb949f56a63c5e134dfdca12091e98ffb5fd446293ebae123d10fc1abad00b9e",
|
"sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb",
|
||||||
"sha256:f07b69fda20578367eaebbd670ff8fc653ab181e1ff95d84497f9fa20e7d0641",
|
"sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063",
|
||||||
"sha256:f95cece33329dc4aa3b0e1a771c41075812e46cf3d6e3f1dfe3d91ff09826ed2"
|
"sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==24.4.0"
|
"version": "==24.4.2"
|
||||||
},
|
},
|
||||||
"blinker": {
|
"blinker": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9",
|
"sha256:5f1cdeff423b77c31b89de0565cd03e5275a03028f44b2b15f912632a58cced6",
|
||||||
"sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"
|
"sha256:da44ec748222dcd0105ef975eed946da197d5bdf8bafb6aa92f5bc89da63fa25"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==1.7.0"
|
"version": "==1.8.1"
|
||||||
},
|
},
|
||||||
"boto3": {
|
"boto3": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2824e3dd18743ca50e5b10439d20e74647b1416e8a94509cb30beac92d27a18d",
|
"sha256:decf52f8d5d8a1b10c9ff2a0e96ee207ed79e33d2e53fdf0880a5cbef70785e0",
|
||||||
"sha256:b2e5cb5b95efcc881e25a3bc872d7a24e75ff4e76f368138e4baf7b9d6ee3422"
|
"sha256:e836b71d79671270fccac0a4d4c8ec239a6b82ea47c399b64675aa597d0ee63b"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==1.34.90"
|
"version": "==1.34.95"
|
||||||
},
|
},
|
||||||
"boto3-mocking": {
|
"boto3-mocking": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1299,28 +1307,28 @@
|
||||||
},
|
},
|
||||||
"boto3-stubs": {
|
"boto3-stubs": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:7361f162523168ddcfb3e0cc70e5208e78f95b9f1f2553032036a2b67ab33355",
|
"sha256:412006b27ee707e9b51a084b02ac92b143af8a3b56727582afec2a76ce93c3b6",
|
||||||
"sha256:c82f3db8558e28f766361ba1eea7c77dff735f72fef2a0b9dffaa9c0d9ae76a3"
|
"sha256:4fb5830626de42446c238ca72ca1a53e461281396007fb900edf50ceeb044a10"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==1.34.90"
|
"version": "==1.34.95"
|
||||||
},
|
},
|
||||||
"botocore": {
|
"botocore": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:113cd4c0cb63e13163ccbc2bb13d551be314ba7f8ba5bfab1c51a19ca01aa133",
|
"sha256:6bd76a2eadb42b91fa3528392e981ad5b4dfdee3968fa5b904278acf6cbf15ff",
|
||||||
"sha256:d48f152498e2c60b43ce25b579d26642346a327b6fb2c632d57219e0a4f63392"
|
"sha256:ead5823e0dd6751ece5498cb979fd9abf190e691c8833bcac6876fd6ca261fa7"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==1.34.90"
|
"version": "==1.34.95"
|
||||||
},
|
},
|
||||||
"botocore-stubs": {
|
"botocore-stubs": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:b2d7416b524bce7325aa5fe09bb5e0b6bc9531d4136f4407fa39b6bc58507f34",
|
"sha256:64d80a3467e3b19939e9c2750af33328b3087f8f524998dbdf7ed168227f507d",
|
||||||
"sha256:d9b66542cbb8fbe28eef3c22caf941ae22d36cc1ef55b93fc0b52239457cab57"
|
"sha256:b0345f55babd8b901c53804fc5c326a4a0bd2e23e3b71f9ea5d9f7663466e6ba"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.8' and python_version < '4.0'",
|
"markers": "python_version >= '3.8' and python_version < '4.0'",
|
||||||
"version": "==1.34.89"
|
"version": "==1.34.94"
|
||||||
},
|
},
|
||||||
"click": {
|
"click": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1357,20 +1365,20 @@
|
||||||
},
|
},
|
||||||
"django-stubs": {
|
"django-stubs": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:4cf4de258fa71adc6f2799e983091b9d46cfc67c6eebc68fe111218c9a62b3b8",
|
"sha256:084484cbe16a6d388e80ec687e46f529d67a232f3befaf55c936b3b476be289d",
|
||||||
"sha256:8ccd2ff4ee5adf22b9e3b7b1a516d2e1c2191e9d94e672c35cc2bc3dd61e0f6b"
|
"sha256:b8a792bee526d6cab31e197cb414ee7fa218abd931a50948c66a80b3a2548621"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==4.2.7"
|
"version": "==5.0.0"
|
||||||
},
|
},
|
||||||
"django-stubs-ext": {
|
"django-stubs-ext": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:45a5d102417a412e3606e3c358adb4744988a92b7b58ccf3fd64bddd5d04d14c",
|
"sha256:5bacfbb498a206d5938454222b843d81da79ea8b6fcd1a59003f529e775bc115",
|
||||||
"sha256:519342ac0849cda1559746c9a563f03ff99f636b0ebe7c14b75e816a00dfddc3"
|
"sha256:8e1334fdf0c8bff87e25d593b33d4247487338aaed943037826244ff788b56a8"
|
||||||
],
|
],
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==4.2.7"
|
"version": "==5.0.0"
|
||||||
},
|
},
|
||||||
"django-webtest": {
|
"django-webtest": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1423,37 +1431,37 @@
|
||||||
},
|
},
|
||||||
"mypy": {
|
"mypy": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:0235391f1c6f6ce487b23b9dbd1327b4ec33bb93934aa986efe8a9563d9349e6",
|
"sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061",
|
||||||
"sha256:190da1ee69b427d7efa8aa0d5e5ccd67a4fb04038c380237a0d96829cb157913",
|
"sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99",
|
||||||
"sha256:2418488264eb41f69cc64a69a745fad4a8f86649af4b1041a4c64ee61fc61129",
|
"sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de",
|
||||||
"sha256:3a3c007ff3ee90f69cf0a15cbcdf0995749569b86b6d2f327af01fd1b8aee9dc",
|
"sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a",
|
||||||
"sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974",
|
"sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9",
|
||||||
"sha256:48533cdd345c3c2e5ef48ba3b0d3880b257b423e7995dada04248725c6f77374",
|
"sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec",
|
||||||
"sha256:49c87c15aed320de9b438ae7b00c1ac91cd393c1b854c2ce538e2a72d55df150",
|
"sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1",
|
||||||
"sha256:4d3dbd346cfec7cb98e6cbb6e0f3c23618af826316188d587d1c1bc34f0ede03",
|
"sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131",
|
||||||
"sha256:571741dc4194b4f82d344b15e8837e8c5fcc462d66d076748142327626a1b6e9",
|
"sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f",
|
||||||
"sha256:587ce887f75dd9700252a3abbc9c97bbe165a4a630597845c61279cf32dfbf02",
|
"sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821",
|
||||||
"sha256:5d741d3fc7c4da608764073089e5f58ef6352bedc223ff58f2f038c2c4698a89",
|
"sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5",
|
||||||
"sha256:5e6061f44f2313b94f920e91b204ec600982961e07a17e0f6cd83371cb23f5c2",
|
"sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee",
|
||||||
"sha256:61758fabd58ce4b0720ae1e2fea5cfd4431591d6d590b197775329264f86311d",
|
"sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e",
|
||||||
"sha256:653265f9a2784db65bfca694d1edd23093ce49740b2244cde583aeb134c008f3",
|
"sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746",
|
||||||
"sha256:68edad3dc7d70f2f17ae4c6c1b9471a56138ca22722487eebacfd1eb5321d612",
|
"sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2",
|
||||||
"sha256:81a10926e5473c5fc3da8abb04119a1f5811a236dc3a38d92015cb1e6ba4cb9e",
|
"sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0",
|
||||||
"sha256:85ca5fcc24f0b4aeedc1d02f93707bccc04733f21d41c88334c5482219b1ccb3",
|
"sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b",
|
||||||
"sha256:a260627a570559181a9ea5de61ac6297aa5af202f06fd7ab093ce74e7181e43e",
|
"sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53",
|
||||||
"sha256:aceb1db093b04db5cd390821464504111b8ec3e351eb85afd1433490163d60cd",
|
"sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30",
|
||||||
"sha256:b685154e22e4e9199fc95f298661deea28aaede5ae16ccc8cbb1045e716b3e04",
|
"sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda",
|
||||||
"sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed",
|
"sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051",
|
||||||
"sha256:d4d5ddc13421ba3e2e082a6c2d74c2ddb3979c39b582dacd53dd5d9431237185",
|
"sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2",
|
||||||
"sha256:e49499be624dead83927e70c756970a0bc8240e9f769389cdf5714b0784ca6bf",
|
"sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7",
|
||||||
"sha256:e54396d70be04b34f31d2edf3362c1edd023246c82f1730bbf8768c28db5361b",
|
"sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee",
|
||||||
"sha256:f88566144752999351725ac623471661c9d1cd8caa0134ff98cceeea181789f4",
|
"sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727",
|
||||||
"sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f",
|
"sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976",
|
||||||
"sha256:fe28657de3bfec596bbeef01cb219833ad9d38dd5393fc649f4b366840baefe6"
|
"sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"markers": "python_version >= '3.8'",
|
"markers": "python_version >= '3.8'",
|
||||||
"version": "==1.9.0"
|
"version": "==1.10.0"
|
||||||
},
|
},
|
||||||
"mypy-extensions": {
|
"mypy-extensions": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
@ -1665,14 +1673,6 @@
|
||||||
"markers": "python_version >= '3.7'",
|
"markers": "python_version >= '3.7'",
|
||||||
"version": "==5.3.0.7"
|
"version": "==5.3.0.7"
|
||||||
},
|
},
|
||||||
"types-pytz": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:6810c8a1f68f21fdf0f4f374a432487c77645a0ac0b31de4bf4690cf21ad3981",
|
|
||||||
"sha256:8335d443310e2db7b74e007414e74c4f53b67452c0cb0d228ca359ccfba59659"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '3.8'",
|
|
||||||
"version": "==2024.1.0.20240417"
|
|
||||||
},
|
|
||||||
"types-pyyaml": {
|
"types-pyyaml": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342",
|
"sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342",
|
||||||
|
|
|
@ -15,6 +15,8 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from dateutil.relativedelta import relativedelta # type: ignore
|
from dateutil.relativedelta import relativedelta # type: ignore
|
||||||
from epplibwrapper.errors import ErrorCode, RegistryError
|
from epplibwrapper.errors import ErrorCode, RegistryError
|
||||||
|
from waffle.admin import FlagAdmin
|
||||||
|
from waffle.models import Sample, Switch
|
||||||
from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website
|
from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website
|
||||||
from registrar.utility.errors import FSMDomainRequestError, FSMErrorCodes
|
from registrar.utility.errors import FSMDomainRequestError, FSMErrorCodes
|
||||||
from registrar.views.utility.mixins import OrderableFieldsMixin
|
from registrar.views.utility.mixins import OrderableFieldsMixin
|
||||||
|
@ -2158,7 +2160,16 @@ class UserGroupAdmin(AuditedAdmin):
|
||||||
return obj.name
|
return obj.name
|
||||||
|
|
||||||
|
|
||||||
|
class WaffleFlagAdmin(FlagAdmin):
|
||||||
|
class Meta:
|
||||||
|
"""Contains meta information about this class"""
|
||||||
|
|
||||||
|
model = models.WaffleFlag
|
||||||
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
admin.site.unregister(LogEntry) # Unregister the default registration
|
admin.site.unregister(LogEntry) # Unregister the default registration
|
||||||
|
|
||||||
admin.site.register(LogEntry, CustomLogEntryAdmin)
|
admin.site.register(LogEntry, CustomLogEntryAdmin)
|
||||||
admin.site.register(models.User, MyUserAdmin)
|
admin.site.register(models.User, MyUserAdmin)
|
||||||
# Unregister the built-in Group model
|
# Unregister the built-in Group model
|
||||||
|
@ -2180,3 +2191,10 @@ admin.site.register(models.PublicContact, PublicContactAdmin)
|
||||||
admin.site.register(models.DomainRequest, DomainRequestAdmin)
|
admin.site.register(models.DomainRequest, DomainRequestAdmin)
|
||||||
admin.site.register(models.TransitionDomain, TransitionDomainAdmin)
|
admin.site.register(models.TransitionDomain, TransitionDomainAdmin)
|
||||||
admin.site.register(models.VerifiedByStaff, VerifiedByStaffAdmin)
|
admin.site.register(models.VerifiedByStaff, VerifiedByStaffAdmin)
|
||||||
|
|
||||||
|
# Register our custom waffle implementations
|
||||||
|
admin.site.register(models.WaffleFlag, WaffleFlagAdmin)
|
||||||
|
|
||||||
|
# Unregister Sample and Switch from the waffle library
|
||||||
|
admin.site.unregister(Sample)
|
||||||
|
admin.site.unregister(Switch)
|
||||||
|
|
|
@ -22,7 +22,6 @@ from base64 import b64decode
|
||||||
from cfenv import AppEnv # type: ignore
|
from cfenv import AppEnv # type: ignore
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
from botocore.config import Config
|
from botocore.config import Config
|
||||||
|
|
||||||
# # # ###
|
# # # ###
|
||||||
|
@ -148,6 +147,8 @@ INSTALLED_APPS = [
|
||||||
"corsheaders",
|
"corsheaders",
|
||||||
# library for multiple choice filters in django admin
|
# library for multiple choice filters in django admin
|
||||||
"django_admin_multiple_choice_list_filter",
|
"django_admin_multiple_choice_list_filter",
|
||||||
|
# Waffle feature flags
|
||||||
|
"waffle",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Middleware are routines for processing web requests.
|
# Middleware are routines for processing web requests.
|
||||||
|
@ -183,6 +184,8 @@ MIDDLEWARE = [
|
||||||
"csp.middleware.CSPMiddleware",
|
"csp.middleware.CSPMiddleware",
|
||||||
# django-auditlog: obtain the request User for use in logging
|
# django-auditlog: obtain the request User for use in logging
|
||||||
"auditlog.middleware.AuditlogMiddleware",
|
"auditlog.middleware.AuditlogMiddleware",
|
||||||
|
# Used for waffle feature flags
|
||||||
|
"waffle.middleware.WaffleMiddleware",
|
||||||
]
|
]
|
||||||
|
|
||||||
# application object used by Django’s built-in servers (e.g. `runserver`)
|
# application object used by Django’s built-in servers (e.g. `runserver`)
|
||||||
|
@ -319,6 +322,17 @@ EMAIL_TIMEOUT = 30
|
||||||
SERVER_EMAIL = "root@get.gov"
|
SERVER_EMAIL = "root@get.gov"
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
|
# region: Waffle feature flags-----------------------------------------------------------###
|
||||||
|
# If Waffle encounters a reference to a flag that is not in the database, should Waffle create the flag?
|
||||||
|
WAFFLE_CREATE_MISSING_FLAGS = True
|
||||||
|
|
||||||
|
# The model that will be used to keep track of flags. Extends AbstractUserFlag.
|
||||||
|
# Used to replace the default flag class (for customization purposes).
|
||||||
|
WAFFLE_FLAG_MODEL = "registrar.WaffleFlag"
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
# region: Headers-----------------------------------------------------------###
|
# region: Headers-----------------------------------------------------------###
|
||||||
|
|
||||||
# Content-Security-Policy configuration
|
# Content-Security-Policy configuration
|
||||||
|
|
|
@ -196,12 +196,12 @@ class UserFixture:
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
def load_users(cls, users, group_name):
|
def load_users(cls, users, group_name, are_superusers=False):
|
||||||
logger.info(f"Going to load {len(users)} users in group {group_name}")
|
logger.info(f"Going to load {len(users)} users in group {group_name}")
|
||||||
for user_data in users:
|
for user_data in users:
|
||||||
try:
|
try:
|
||||||
user, _ = User.objects.get_or_create(username=user_data["username"])
|
user, _ = User.objects.get_or_create(username=user_data["username"])
|
||||||
user.is_superuser = False
|
user.is_superuser = are_superusers
|
||||||
user.first_name = user_data["first_name"]
|
user.first_name = user_data["first_name"]
|
||||||
user.last_name = user_data["last_name"]
|
user.last_name = user_data["last_name"]
|
||||||
if "email" in user_data:
|
if "email" in user_data:
|
||||||
|
@ -229,5 +229,5 @@ class UserFixture:
|
||||||
# steps now do not need to close/reopen a db connection,
|
# steps now do not need to close/reopen a db connection,
|
||||||
# instead they share one.
|
# instead they share one.
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
cls.load_users(cls, cls.ADMINS, "full_access_group")
|
cls.load_users(cls, cls.ADMINS, "full_access_group", are_superusers=True)
|
||||||
cls.load_users(cls, cls.STAFF, "cisa_analysts_group")
|
cls.load_users(cls, cls.STAFF, "cisa_analysts_group")
|
||||||
|
|
127
src/registrar/migrations/0090_waffleflag.py
Normal file
127
src/registrar/migrations/0090_waffleflag.py
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
# Generated by Django 4.2.10 on 2024-05-02 17:47
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("auth", "0012_alter_user_first_name_max_length"),
|
||||||
|
("registrar", "0089_user_verification_type"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="WaffleFlag",
|
||||||
|
fields=[
|
||||||
|
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
|
||||||
|
(
|
||||||
|
"name",
|
||||||
|
models.CharField(
|
||||||
|
help_text="The human/computer readable name.", max_length=100, unique=True, verbose_name="Name"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"everyone",
|
||||||
|
models.BooleanField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Flip this flag on (Yes) or off (No) for everyone, overriding all other settings. Leave as Unknown to use normally.",
|
||||||
|
null=True,
|
||||||
|
verbose_name="Everyone",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"percent",
|
||||||
|
models.DecimalField(
|
||||||
|
blank=True,
|
||||||
|
decimal_places=1,
|
||||||
|
help_text="A number between 0.0 and 99.9 to indicate a percentage of users for whom this flag will be active.",
|
||||||
|
max_digits=3,
|
||||||
|
null=True,
|
||||||
|
verbose_name="Percent",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"testing",
|
||||||
|
models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Allow this flag to be set for a session for user testing",
|
||||||
|
verbose_name="Testing",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"superusers",
|
||||||
|
models.BooleanField(
|
||||||
|
default=True, help_text="Flag always active for superusers?", verbose_name="Superusers"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"staff",
|
||||||
|
models.BooleanField(default=False, help_text="Flag always active for staff?", verbose_name="Staff"),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"authenticated",
|
||||||
|
models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Flag always active for authenticated users?",
|
||||||
|
verbose_name="Authenticated",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"languages",
|
||||||
|
models.TextField(
|
||||||
|
blank=True,
|
||||||
|
default="",
|
||||||
|
help_text="Activate this flag for users with one of these languages (comma-separated list)",
|
||||||
|
verbose_name="Languages",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"rollout",
|
||||||
|
models.BooleanField(default=False, help_text="Activate roll-out mode?", verbose_name="Rollout"),
|
||||||
|
),
|
||||||
|
("note", models.TextField(blank=True, help_text="Note where this Flag is used.", verbose_name="Note")),
|
||||||
|
(
|
||||||
|
"created",
|
||||||
|
models.DateTimeField(
|
||||||
|
db_index=True,
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
help_text="Date when this Flag was created.",
|
||||||
|
verbose_name="Created",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"modified",
|
||||||
|
models.DateTimeField(
|
||||||
|
default=django.utils.timezone.now,
|
||||||
|
help_text="Date when this Flag was last modified.",
|
||||||
|
verbose_name="Modified",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"groups",
|
||||||
|
models.ManyToManyField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Activate this flag for these user groups.",
|
||||||
|
to="auth.group",
|
||||||
|
verbose_name="Groups",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"users",
|
||||||
|
models.ManyToManyField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Activate this flag for these users.",
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
verbose_name="Users",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "waffle flag",
|
||||||
|
"verbose_name_plural": "Waffle flags",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -15,6 +15,8 @@ from .user_group import UserGroup
|
||||||
from .website import Website
|
from .website import Website
|
||||||
from .transition_domain import TransitionDomain
|
from .transition_domain import TransitionDomain
|
||||||
from .verified_by_staff import VerifiedByStaff
|
from .verified_by_staff import VerifiedByStaff
|
||||||
|
from .waffle_flag import WaffleFlag
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Contact",
|
"Contact",
|
||||||
|
@ -33,6 +35,7 @@ __all__ = [
|
||||||
"Website",
|
"Website",
|
||||||
"TransitionDomain",
|
"TransitionDomain",
|
||||||
"VerifiedByStaff",
|
"VerifiedByStaff",
|
||||||
|
"WaffleFlag",
|
||||||
]
|
]
|
||||||
|
|
||||||
auditlog.register(Contact)
|
auditlog.register(Contact)
|
||||||
|
@ -51,3 +54,4 @@ auditlog.register(UserGroup, m2m_fields=["permissions"])
|
||||||
auditlog.register(Website)
|
auditlog.register(Website)
|
||||||
auditlog.register(TransitionDomain)
|
auditlog.register(TransitionDomain)
|
||||||
auditlog.register(VerifiedByStaff)
|
auditlog.register(VerifiedByStaff)
|
||||||
|
auditlog.register(WaffleFlag)
|
||||||
|
|
19
src/registrar/models/waffle_flag.py
Normal file
19
src/registrar/models/waffle_flag.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
from waffle.models import AbstractUserFlag
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class WaffleFlag(AbstractUserFlag):
|
||||||
|
"""
|
||||||
|
Custom implementation of django-waffles 'Flag' object.
|
||||||
|
Read more here: https://waffle.readthedocs.io/en/stable/types/flag.html
|
||||||
|
|
||||||
|
Use this class when dealing with feature flags, such as profile_feature.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Contains meta information about this class"""
|
||||||
|
|
||||||
|
verbose_name = "waffle flag"
|
||||||
|
verbose_name_plural = "Waffle flags"
|
|
@ -1,8 +1,10 @@
|
||||||
<div class="usa-site-alert--emergency margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert"
|
<div class="usa-site-alert--emergency margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||||
>
|
|
||||||
<div class="usa-alert">
|
<div class="usa-alert">
|
||||||
<div class="usa-alert__body {% if add_body_class %}{{ add_body_class }}{% endif %}">
|
<div class="usa-alert__body {% if add_body_class %}{{ add_body_class }}{% endif %}">
|
||||||
<b>Attention:</b> You are on a test site.
|
<b>Attention:</b> You are on a test site.
|
||||||
|
{% if has_profile_feature_flag %}
|
||||||
|
The profile_feature flag is active.
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -1,6 +1,7 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
|
||||||
from registrar.models import DomainRequest, Domain, UserDomainRole
|
from registrar.models import DomainRequest, Domain, UserDomainRole
|
||||||
|
from waffle.decorators import flag_is_active
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
|
@ -20,6 +21,9 @@ def index(request):
|
||||||
has_deletable_domain_requests = deletable_domain_requests.exists()
|
has_deletable_domain_requests = deletable_domain_requests.exists()
|
||||||
context["has_deletable_domain_requests"] = has_deletable_domain_requests
|
context["has_deletable_domain_requests"] = has_deletable_domain_requests
|
||||||
|
|
||||||
|
# This is a django waffle flag which toggles features based off of the "flag" table
|
||||||
|
context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature")
|
||||||
|
|
||||||
# If they can delete domain requests, add the delete button to the context
|
# If they can delete domain requests, add the delete button to the context
|
||||||
if has_deletable_domain_requests:
|
if has_deletable_domain_requests:
|
||||||
# Add the delete modal button to the context
|
# Add the delete modal button to the context
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
-i https://pypi.python.org/simple
|
-i https://pypi.python.org/simple
|
||||||
annotated-types==0.6.0; python_version >= '3.8'
|
annotated-types==0.6.0; python_version >= '3.8'
|
||||||
asgiref==3.8.1; python_version >= '3.8'
|
asgiref==3.8.1; python_version >= '3.8'
|
||||||
boto3==1.34.90; python_version >= '3.8'
|
boto3==1.34.95; python_version >= '3.8'
|
||||||
botocore==1.34.90; python_version >= '3.8'
|
botocore==1.34.95; python_version >= '3.8'
|
||||||
cachetools==5.3.3; python_version >= '3.7'
|
cachetools==5.3.3; python_version >= '3.7'
|
||||||
certifi==2024.2.2; python_version >= '3.6'
|
certifi==2024.2.2; python_version >= '3.6'
|
||||||
cfenv==0.5.3
|
cfenv==0.5.3
|
||||||
|
@ -22,9 +22,10 @@ django-csp==3.8
|
||||||
django-fsm==2.8.1
|
django-fsm==2.8.1
|
||||||
django-login-required-middleware==0.9.0
|
django-login-required-middleware==0.9.0
|
||||||
django-phonenumber-field[phonenumberslite]==7.3.0; python_version >= '3.8'
|
django-phonenumber-field[phonenumberslite]==7.3.0; python_version >= '3.8'
|
||||||
|
django-waffle==4.1.0; python_version >= '3.8'
|
||||||
django-widget-tweaks==1.5.0; python_version >= '3.8'
|
django-widget-tweaks==1.5.0; python_version >= '3.8'
|
||||||
environs[django]==11.0.0; python_version >= '3.8'
|
environs[django]==11.0.0; python_version >= '3.8'
|
||||||
faker==24.11.0; python_version >= '3.8'
|
faker==25.0.0; python_version >= '3.8'
|
||||||
fred-epplib@ git+https://github.com/cisagov/epplib.git@d56d183f1664f34c40ca9716a3a9a345f0ef561c
|
fred-epplib@ git+https://github.com/cisagov/epplib.git@d56d183f1664f34c40ca9716a3a9a345f0ef561c
|
||||||
furl==2.1.3
|
furl==2.1.3
|
||||||
future==1.0.0; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
future==1.0.0; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'
|
||||||
|
@ -37,7 +38,7 @@ lxml==5.2.1; python_version >= '3.6'
|
||||||
mako==1.3.3; python_version >= '3.8'
|
mako==1.3.3; python_version >= '3.8'
|
||||||
markupsafe==2.1.5; python_version >= '3.7'
|
markupsafe==2.1.5; python_version >= '3.7'
|
||||||
marshmallow==3.21.1; python_version >= '3.8'
|
marshmallow==3.21.1; python_version >= '3.8'
|
||||||
oic==1.6.1; python_version ~= '3.7'
|
oic==1.7.0; python_version ~= '3.8'
|
||||||
orderedmultidict==1.0.1
|
orderedmultidict==1.0.1
|
||||||
packaging==24.0; python_version >= '3.7'
|
packaging==24.0; python_version >= '3.7'
|
||||||
phonenumberslite==8.13.35
|
phonenumberslite==8.13.35
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue