chore: Merge pull request #5509 from larseggert/feat-dark-mode
feat: Upgrade bootstrap to 5.3.0 and enable dark mode
This commit is contained in:
commit
ce3114c6eb
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
|
@ -91,10 +91,10 @@ jobs:
|
|||
echo "pkg_version=$NEXT_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "::notice::Release $NEXT_VERSION created using branch $GITHUB_REF_NAME"
|
||||
else
|
||||
echo "Using TEST mode: 10.0.0-dev.$GITHUB_RUN_NUMBER"
|
||||
echo "Using TEST mode: 11.0.0-dev.$GITHUB_RUN_NUMBER"
|
||||
echo "should_deploy=false" >> $GITHUB_OUTPUT
|
||||
echo "pkg_version=10.0.0-dev.$GITHUB_RUN_NUMBER" >> $GITHUB_OUTPUT
|
||||
echo "::notice::Non-production build 10.0.0-dev.$GITHUB_RUN_NUMBER created using branch $GITHUB_REF_NAME"
|
||||
echo "pkg_version=11.0.0-dev.$GITHUB_RUN_NUMBER" >> $GITHUB_OUTPUT
|
||||
echo "::notice::Non-production build 11.0.0-dev.$GITHUB_RUN_NUMBER created using branch $GITHUB_REF_NAME"
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
|
|
1
.github/workflows/ci-run-tests.yml
vendored
1
.github/workflows/ci-run-tests.yml
vendored
|
@ -4,6 +4,7 @@ on:
|
|||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'feat/django4'
|
||||
paths:
|
||||
- 'client/**'
|
||||
- 'ietf/**'
|
||||
|
|
194
.pnp.cjs
generated
194
.pnp.cjs
generated
|
@ -34,15 +34,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
"packageLocation": "./",\
|
||||
"packageDependencies": [\
|
||||
["@faker-js/faker", "npm:8.0.1"],\
|
||||
["@fullcalendar/bootstrap5", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@fullcalendar/daygrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.6"],\
|
||||
["@fullcalendar/icalendar", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/interaction", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/list", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/luxon2", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/timegrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/vue3", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/bootstrap5", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/core", "npm:6.1.8"],\
|
||||
["@fullcalendar/daygrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/icalendar", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/interaction", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/list", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/luxon3", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/timegrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/vue3", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@parcel/optimizer-data-url", "npm:2.8.3"],\
|
||||
["@parcel/transformer-inline-string", "npm:2.8.3"],\
|
||||
["@parcel/transformer-sass", "npm:2.8.3"],\
|
||||
|
@ -50,7 +50,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
["@rollup/pluginutils", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.0.2"],\
|
||||
["@twuni/emojify", "npm:1.0.2"],\
|
||||
["@vitejs/plugin-vue", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:4.2.1"],\
|
||||
["bootstrap", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.2.3"],\
|
||||
["bootstrap", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.3.0"],\
|
||||
["bootstrap-icons", "npm:1.10.5"],\
|
||||
["browser-fs-access", "npm:0.33.1"],\
|
||||
["browserlist", "npm:1.0.1"],\
|
||||
|
@ -502,18 +502,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
}]\
|
||||
]],\
|
||||
["@fullcalendar/bootstrap5", [\
|
||||
["npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-bootstrap5-npm-6.1.7-6c442ff939-25783fec80.zip/node_modules/@fullcalendar/bootstrap5/",\
|
||||
["npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-bootstrap5-npm-6.1.8-bbeae5dafc-c78ef0d62e.zip/node_modules/@fullcalendar/bootstrap5/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/bootstrap5", "npm:6.1.7"]\
|
||||
["@fullcalendar/bootstrap5", "npm:6.1.8"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-bootstrap5-virtual-d641a1c9f9/0/cache/@fullcalendar-bootstrap5-npm-6.1.7-6c442ff939-25783fec80.zip/node_modules/@fullcalendar/bootstrap5/",\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-bootstrap5-virtual-d0fea3107b/0/cache/@fullcalendar-bootstrap5-npm-6.1.8-bbeae5dafc-c78ef0d62e.zip/node_modules/@fullcalendar/bootstrap5/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/bootstrap5", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@fullcalendar/bootstrap5", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/core", "npm:6.1.8"],\
|
||||
["@types/fullcalendar__core", null]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
|
@ -524,48 +524,28 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
}]\
|
||||
]],\
|
||||
["@fullcalendar/core", [\
|
||||
["npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-core-npm-6.1.7-f021bcbbf8-d42f0f069b.zip/node_modules/@fullcalendar/core/",\
|
||||
["npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-core-npm-6.1.8-da04efa804-66c13078c9.zip/node_modules/@fullcalendar/core/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@fullcalendar/core", "npm:6.1.8"],\
|
||||
["preact", "npm:10.12.1"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@fullcalendar/daygrid", [\
|
||||
["npm:6.1.6", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-daygrid-npm-6.1.6-13b72a08b0-e7b60e359b.zip/node_modules/@fullcalendar/daygrid/",\
|
||||
["npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-daygrid-npm-6.1.8-3f45184389-a99441c81d.zip/node_modules/@fullcalendar/daygrid/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/daygrid", "npm:6.1.6"]\
|
||||
["@fullcalendar/daygrid", "npm:6.1.8"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-daygrid-npm-6.1.7-bb16b58bd9-6f5e06d105.zip/node_modules/@fullcalendar/daygrid/",\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-daygrid-virtual-77be6a5058/0/cache/@fullcalendar-daygrid-npm-6.1.8-3f45184389-a99441c81d.zip/node_modules/@fullcalendar/daygrid/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/daygrid", "npm:6.1.7"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:4132da971e14ad1fe0fd08826ee5e453ecc047f5188755393fdd3a41ea53d3bda87f2afc98efffec56468b1f7ef290b0d7f888c8638abb2171593e375bb65ba1#npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-daygrid-virtual-a258a3200c/0/cache/@fullcalendar-daygrid-npm-6.1.7-bb16b58bd9-6f5e06d105.zip/node_modules/@fullcalendar/daygrid/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/daygrid", "virtual:4132da971e14ad1fe0fd08826ee5e453ecc047f5188755393fdd3a41ea53d3bda87f2afc98efffec56468b1f7ef290b0d7f888c8638abb2171593e375bb65ba1#npm:6.1.7"],\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@types/fullcalendar__core", null]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
"@fullcalendar/core",\
|
||||
"@types/fullcalendar__core"\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.6", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-daygrid-virtual-c806a244df/0/cache/@fullcalendar-daygrid-npm-6.1.6-13b72a08b0-e7b60e359b.zip/node_modules/@fullcalendar/daygrid/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/daygrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.6"],\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@fullcalendar/daygrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/core", "npm:6.1.8"],\
|
||||
["@types/fullcalendar__core", null]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
|
@ -576,18 +556,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
}]\
|
||||
]],\
|
||||
["@fullcalendar/icalendar", [\
|
||||
["npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-icalendar-npm-6.1.7-87629ada23-de621be062.zip/node_modules/@fullcalendar/icalendar/",\
|
||||
["npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-icalendar-npm-6.1.8-bee329d052-f322ce54bb.zip/node_modules/@fullcalendar/icalendar/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/icalendar", "npm:6.1.7"]\
|
||||
["@fullcalendar/icalendar", "npm:6.1.8"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-icalendar-virtual-1bfdcc5d9c/0/cache/@fullcalendar-icalendar-npm-6.1.7-87629ada23-de621be062.zip/node_modules/@fullcalendar/icalendar/",\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-icalendar-virtual-0660ccc07e/0/cache/@fullcalendar-icalendar-npm-6.1.8-bee329d052-f322ce54bb.zip/node_modules/@fullcalendar/icalendar/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/icalendar", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@fullcalendar/icalendar", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/core", "npm:6.1.8"],\
|
||||
["@types/fullcalendar__core", null],\
|
||||
["@types/ical.js", null],\
|
||||
["ical.js", "npm:1.5.0"]\
|
||||
|
@ -602,18 +582,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
}]\
|
||||
]],\
|
||||
["@fullcalendar/interaction", [\
|
||||
["npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-interaction-npm-6.1.7-c7a3547608-ee72c9cbc2.zip/node_modules/@fullcalendar/interaction/",\
|
||||
["npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-interaction-npm-6.1.8-6c6b6987db-3ef0da6dca.zip/node_modules/@fullcalendar/interaction/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/interaction", "npm:6.1.7"]\
|
||||
["@fullcalendar/interaction", "npm:6.1.8"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-interaction-virtual-3563939498/0/cache/@fullcalendar-interaction-npm-6.1.7-c7a3547608-ee72c9cbc2.zip/node_modules/@fullcalendar/interaction/",\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-interaction-virtual-39f9dba62d/0/cache/@fullcalendar-interaction-npm-6.1.8-6c6b6987db-3ef0da6dca.zip/node_modules/@fullcalendar/interaction/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/interaction", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@fullcalendar/interaction", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/core", "npm:6.1.8"],\
|
||||
["@types/fullcalendar__core", null]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
|
@ -624,18 +604,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
}]\
|
||||
]],\
|
||||
["@fullcalendar/list", [\
|
||||
["npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-list-npm-6.1.7-197e29fffe-a435caf918.zip/node_modules/@fullcalendar/list/",\
|
||||
["npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-list-npm-6.1.8-39b471f8da-b5c397040e.zip/node_modules/@fullcalendar/list/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/list", "npm:6.1.7"]\
|
||||
["@fullcalendar/list", "npm:6.1.8"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-list-virtual-83328d51c1/0/cache/@fullcalendar-list-npm-6.1.7-197e29fffe-a435caf918.zip/node_modules/@fullcalendar/list/",\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-list-virtual-a4f877cc68/0/cache/@fullcalendar-list-npm-6.1.8-39b471f8da-b5c397040e.zip/node_modules/@fullcalendar/list/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/list", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@fullcalendar/list", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/core", "npm:6.1.8"],\
|
||||
["@types/fullcalendar__core", null]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
|
@ -645,19 +625,19 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@fullcalendar/luxon2", [\
|
||||
["npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-luxon2-npm-6.1.7-5de0c768c0-ddbf0075b3.zip/node_modules/@fullcalendar/luxon2/",\
|
||||
["@fullcalendar/luxon3", [\
|
||||
["npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-luxon3-npm-6.1.8-7f233a53e1-7e84200641.zip/node_modules/@fullcalendar/luxon3/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/luxon2", "npm:6.1.7"]\
|
||||
["@fullcalendar/luxon3", "npm:6.1.8"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-luxon2-virtual-66f46ee050/0/cache/@fullcalendar-luxon2-npm-6.1.7-5de0c768c0-ddbf0075b3.zip/node_modules/@fullcalendar/luxon2/",\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-luxon3-virtual-3a7af6083c/0/cache/@fullcalendar-luxon3-npm-6.1.8-7f233a53e1-7e84200641.zip/node_modules/@fullcalendar/luxon3/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/luxon2", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@fullcalendar/luxon3", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/core", "npm:6.1.8"],\
|
||||
["@types/fullcalendar__core", null],\
|
||||
["@types/luxon", null],\
|
||||
["luxon", "npm:3.3.0"]\
|
||||
|
@ -672,19 +652,19 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
}]\
|
||||
]],\
|
||||
["@fullcalendar/timegrid", [\
|
||||
["npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-timegrid-npm-6.1.7-02f357f08e-7455aa0391.zip/node_modules/@fullcalendar/timegrid/",\
|
||||
["npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-timegrid-npm-6.1.8-22d8c05e30-122786fd40.zip/node_modules/@fullcalendar/timegrid/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/timegrid", "npm:6.1.7"]\
|
||||
["@fullcalendar/timegrid", "npm:6.1.8"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-timegrid-virtual-4132da971e/0/cache/@fullcalendar-timegrid-npm-6.1.7-02f357f08e-7455aa0391.zip/node_modules/@fullcalendar/timegrid/",\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-timegrid-virtual-768bdb99c1/0/cache/@fullcalendar-timegrid-npm-6.1.8-22d8c05e30-122786fd40.zip/node_modules/@fullcalendar/timegrid/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/timegrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@fullcalendar/daygrid", "virtual:4132da971e14ad1fe0fd08826ee5e453ecc047f5188755393fdd3a41ea53d3bda87f2afc98efffec56468b1f7ef290b0d7f888c8638abb2171593e375bb65ba1#npm:6.1.7"],\
|
||||
["@fullcalendar/timegrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/core", "npm:6.1.8"],\
|
||||
["@fullcalendar/daygrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@types/fullcalendar__core", null]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
|
@ -695,18 +675,18 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
}]\
|
||||
]],\
|
||||
["@fullcalendar/vue3", [\
|
||||
["npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-vue3-npm-6.1.7-82f4bfa1dd-da6ef3897e.zip/node_modules/@fullcalendar/vue3/",\
|
||||
["npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/cache/@fullcalendar-vue3-npm-6.1.8-a4963d0029-cff81d98ae.zip/node_modules/@fullcalendar/vue3/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/vue3", "npm:6.1.7"]\
|
||||
["@fullcalendar/vue3", "npm:6.1.8"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-vue3-virtual-4929c05b77/0/cache/@fullcalendar-vue3-npm-6.1.7-82f4bfa1dd-da6ef3897e.zip/node_modules/@fullcalendar/vue3/",\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8", {\
|
||||
"packageLocation": "./.yarn/__virtual__/@fullcalendar-vue3-virtual-f86317f38e/0/cache/@fullcalendar-vue3-npm-6.1.8-a4963d0029-cff81d98ae.zip/node_modules/@fullcalendar/vue3/",\
|
||||
"packageDependencies": [\
|
||||
["@fullcalendar/vue3", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@fullcalendar/vue3", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/core", "npm:6.1.8"],\
|
||||
["@types/fullcalendar__core", null],\
|
||||
["@types/vue", null],\
|
||||
["vue", "npm:3.2.47"]\
|
||||
|
@ -2960,10 +2940,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["npm:5.2.3", {\
|
||||
"packageLocation": "./.yarn/cache/bootstrap-npm-5.2.3-7458283a23-0211805dec.zip/node_modules/bootstrap/",\
|
||||
["npm:5.3.0", {\
|
||||
"packageLocation": "./.yarn/cache/bootstrap-npm-5.3.0-240c38a3b2-29a83cc8ca.zip/node_modules/bootstrap/",\
|
||||
"packageDependencies": [\
|
||||
["bootstrap", "npm:5.2.3"]\
|
||||
["bootstrap", "npm:5.3.0"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
|
@ -2980,10 +2960,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.2.3", {\
|
||||
"packageLocation": "./.yarn/__virtual__/bootstrap-virtual-c4952ffff0/0/cache/bootstrap-npm-5.2.3-7458283a23-0211805dec.zip/node_modules/bootstrap/",\
|
||||
["virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.3.0", {\
|
||||
"packageLocation": "./.yarn/__virtual__/bootstrap-virtual-3c63ba6f80/0/cache/bootstrap-npm-5.3.0-240c38a3b2-29a83cc8ca.zip/node_modules/bootstrap/",\
|
||||
"packageDependencies": [\
|
||||
["bootstrap", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.2.3"],\
|
||||
["bootstrap", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.3.0"],\
|
||||
["@popperjs/core", "npm:2.11.7"],\
|
||||
["@types/popperjs__core", null]\
|
||||
],\
|
||||
|
@ -7732,15 +7712,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
"packageDependencies": [\
|
||||
["root-workspace-0b6124", "workspace:."],\
|
||||
["@faker-js/faker", "npm:8.0.1"],\
|
||||
["@fullcalendar/bootstrap5", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/core", "npm:6.1.7"],\
|
||||
["@fullcalendar/daygrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.6"],\
|
||||
["@fullcalendar/icalendar", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/interaction", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/list", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/luxon2", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/timegrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/vue3", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.7"],\
|
||||
["@fullcalendar/bootstrap5", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/core", "npm:6.1.8"],\
|
||||
["@fullcalendar/daygrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/icalendar", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/interaction", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/list", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/luxon3", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/timegrid", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@fullcalendar/vue3", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:6.1.8"],\
|
||||
["@parcel/optimizer-data-url", "npm:2.8.3"],\
|
||||
["@parcel/transformer-inline-string", "npm:2.8.3"],\
|
||||
["@parcel/transformer-sass", "npm:2.8.3"],\
|
||||
|
@ -7748,7 +7728,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
|||
["@rollup/pluginutils", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.0.2"],\
|
||||
["@twuni/emojify", "npm:1.0.2"],\
|
||||
["@vitejs/plugin-vue", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:4.2.1"],\
|
||||
["bootstrap", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.2.3"],\
|
||||
["bootstrap", "virtual:dc3fc578bfa5e06182a4d2be39ede0bc5b74940b1ffe0d70c26892ab140a4699787750fba175dc306292e80b4aa2c8c5f68c2a821e69b2c37e360c0dff36ff66#npm:5.3.0"],\
|
||||
["bootstrap-icons", "npm:1.10.5"],\
|
||||
["browser-fs-access", "npm:0.33.1"],\
|
||||
["browserlist", "npm:1.0.1"],\
|
||||
|
|
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@fullcalendar-core-npm-6.1.8-da04efa804-66c13078c9.zip
vendored
Normal file
BIN
.yarn/cache/@fullcalendar-core-npm-6.1.8-da04efa804-66c13078c9.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@fullcalendar-list-npm-6.1.8-39b471f8da-b5c397040e.zip
vendored
Normal file
BIN
.yarn/cache/@fullcalendar-list-npm-6.1.8-39b471f8da-b5c397040e.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@fullcalendar-luxon3-npm-6.1.8-7f233a53e1-7e84200641.zip
vendored
Normal file
BIN
.yarn/cache/@fullcalendar-luxon3-npm-6.1.8-7f233a53e1-7e84200641.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@fullcalendar-timegrid-npm-6.1.8-22d8c05e30-122786fd40.zip
vendored
Normal file
BIN
.yarn/cache/@fullcalendar-timegrid-npm-6.1.8-22d8c05e30-122786fd40.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/bootstrap-npm-5.3.0-240c38a3b2-29a83cc8ca.zip
vendored
Normal file
BIN
.yarn/cache/bootstrap-npm-5.3.0-240c38a3b2-29a83cc8ca.zip
vendored
Normal file
Binary file not shown.
|
@ -84,7 +84,7 @@ import {
|
|||
import FullCalendar from '@fullcalendar/vue3'
|
||||
import timeGridPlugin from '@fullcalendar/timegrid'
|
||||
import interactionPlugin from '@fullcalendar/interaction'
|
||||
import luxonPlugin from '@fullcalendar/luxon2'
|
||||
import luxonPlugin from '@fullcalendar/luxon3'
|
||||
import bootstrap5Plugin from '@fullcalendar/bootstrap5'
|
||||
|
||||
import AgendaDetailsModal from './AgendaDetailsModal.vue'
|
||||
|
|
|
@ -10,7 +10,7 @@ DATABASES = {
|
|||
'HOST': '__DBHOST__',
|
||||
'PORT': 5432,
|
||||
'NAME': 'datatracker',
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'USER': 'django',
|
||||
'PASSWORD': 'RkTkDPFnKpko',
|
||||
},
|
||||
|
|
|
@ -10,7 +10,7 @@ DATABASES = {
|
|||
'HOST': '__DBHOST__',
|
||||
'PORT': 5432,
|
||||
'NAME': 'datatracker',
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'USER': 'django',
|
||||
'PASSWORD': 'RkTkDPFnKpko',
|
||||
},
|
||||
|
|
|
@ -10,7 +10,7 @@ DATABASES = {
|
|||
'HOST': 'db',
|
||||
'PORT': 5432,
|
||||
'NAME': 'datatracker',
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'USER': 'django',
|
||||
'PASSWORD': 'RkTkDPFnKpko',
|
||||
},
|
||||
|
|
|
@ -3,7 +3,7 @@ DATABASES = {
|
|||
'HOST': 'db',
|
||||
'PORT': 5432,
|
||||
'NAME': 'datatracker',
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'USER': 'django',
|
||||
'PASSWORD': 'RkTkDPFnKpko',
|
||||
},
|
||||
|
|
|
@ -6,7 +6,7 @@ from . import checks # pyflakes:ignore
|
|||
|
||||
# Version must stay in single quotes for automatic CI replace
|
||||
# Don't add patch number here:
|
||||
__version__ = '10.0.0-dev'
|
||||
__version__ = '11.0.0-dev'
|
||||
|
||||
# Release hash must stay in single quotes for automatic CI replace
|
||||
__release_hash__ = ''
|
||||
|
|
|
@ -9,11 +9,12 @@ from django.core.cache import cache
|
|||
from django.core.exceptions import ObjectDoesNotExist, FieldError
|
||||
from django.core.serializers.json import Serializer
|
||||
from django.http import HttpResponse
|
||||
from django.utils.encoding import smart_text
|
||||
from django.utils.encoding import smart_str
|
||||
from django.db.models import Field
|
||||
from django.db.models.query import QuerySet
|
||||
from django.db.models.signals import post_save, post_delete, m2m_changed
|
||||
|
||||
from django_stubs_ext import QuerySetAny
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
||||
|
@ -121,7 +122,7 @@ class AdminJsonSerializer(Serializer):
|
|||
for name in expansions:
|
||||
try:
|
||||
field = getattr(obj, name)
|
||||
#self._current["_"+name] = smart_text(field)
|
||||
#self._current["_"+name] = smart_str(field)
|
||||
if not isinstance(field, Field):
|
||||
options = self.options.copy()
|
||||
options["expand"] = [ v[len(name)+2:] for v in options["expand"] if v.startswith(name+"__") ]
|
||||
|
@ -145,7 +146,7 @@ class AdminJsonSerializer(Serializer):
|
|||
field_value = None
|
||||
else:
|
||||
field_value = field
|
||||
if isinstance(field_value, QuerySet) or isinstance(field_value, list):
|
||||
if isinstance(field_value, QuerySetAny) or isinstance(field_value, list):
|
||||
self._current[name] = dict([ (rel.pk, self.expand_related(rel, name)) for rel in field_value ])
|
||||
else:
|
||||
if hasattr(field_value, "_meta"):
|
||||
|
@ -188,10 +189,10 @@ class AdminJsonSerializer(Serializer):
|
|||
related = related.natural_key()
|
||||
elif field.remote_field.field_name == related._meta.pk.name:
|
||||
# Related to remote object via primary key
|
||||
related = smart_text(related._get_pk_val(), strings_only=True)
|
||||
related = smart_str(related._get_pk_val(), strings_only=True)
|
||||
else:
|
||||
# Related to remote object via other field
|
||||
related = smart_text(getattr(related, field.remote_field.field_name), strings_only=True)
|
||||
related = smart_str(getattr(related, field.remote_field.field_name), strings_only=True)
|
||||
self._current[field.name] = related
|
||||
|
||||
def handle_m2m_field(self, obj, field):
|
||||
|
@ -201,7 +202,7 @@ class AdminJsonSerializer(Serializer):
|
|||
elif self.use_natural_keys and hasattr(field.remote_field.to, 'natural_key'):
|
||||
m2m_value = lambda value: value.natural_key()
|
||||
else:
|
||||
m2m_value = lambda value: smart_text(value._get_pk_val(), strings_only=True)
|
||||
m2m_value = lambda value: smart_str(value._get_pk_val(), strings_only=True)
|
||||
self._current[field.name] = [m2m_value(related)
|
||||
for related in getattr(obj, field.name).iterator()]
|
||||
|
||||
|
@ -221,7 +222,7 @@ class JsonExportMixin(object):
|
|||
# obj = None
|
||||
#
|
||||
# if obj is None:
|
||||
# raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_text(self.model._meta.verbose_name), 'key': escape(object_id)})
|
||||
# raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_str(self.model._meta.verbose_name), 'key': escape(object_id)})
|
||||
#
|
||||
# content_type = 'application/json'
|
||||
# return HttpResponse(serialize([ obj ], sort_keys=True, indent=3)[2:-2], content_type=content_type)
|
||||
|
@ -264,6 +265,6 @@ class JsonExportMixin(object):
|
|||
qd = dict( ( k, json.loads(v)[0] ) for k,v in items )
|
||||
except (FieldError, ValueError) as e:
|
||||
return HttpResponse(json.dumps({"error": str(e)}, sort_keys=True, indent=3), content_type=content_type)
|
||||
text = json.dumps({smart_text(self.model._meta): qd}, sort_keys=True, indent=3)
|
||||
text = json.dumps({smart_str(self.model._meta): qd}, sort_keys=True, indent=3)
|
||||
return HttpResponse(text, content_type=content_type)
|
||||
|
||||
|
|
|
@ -691,7 +691,7 @@ class CustomApiTests(TestCase):
|
|||
self.assertEqual(set(missing_fields), set(drop_fields))
|
||||
|
||||
def test_api_version(self):
|
||||
DumpInfo.objects.create(date=timezone.datetime(2022,8,31,7,10,1,tzinfo=timezone.utc), host='testapi.example.com',tz='UTC')
|
||||
DumpInfo.objects.create(date=timezone.datetime(2022,8,31,7,10,1,tzinfo=datetime.timezone.utc), host='testapi.example.com',tz='UTC')
|
||||
url = urlreverse('ietf.api.views.version')
|
||||
r = self.client.get(url)
|
||||
data = r.json()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright The IETF Trust 2017, All Rights Reserved
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include
|
||||
from django.urls import include
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from ietf import api
|
||||
|
|
|
@ -22,6 +22,7 @@ from ietf.community.utils import docs_tracked_by_community_list, docs_matching_c
|
|||
from ietf.community.utils import states_of_significant_change, reset_name_contains_index_for_rule
|
||||
from ietf.doc.models import DocEvent, Document
|
||||
from ietf.doc.utils_search import prepare_document_table
|
||||
from ietf.utils.http import is_ajax
|
||||
from ietf.utils.response import permission_denied
|
||||
|
||||
def view_list(request, username=None):
|
||||
|
@ -142,7 +143,7 @@ def track_document(request, name, username=None, acronym=None):
|
|||
if not doc in clist.added_docs.all():
|
||||
clist.added_docs.add(doc)
|
||||
|
||||
if request.is_ajax():
|
||||
if is_ajax(request):
|
||||
return HttpResponse(json.dumps({ 'success': True }), content_type='application/json')
|
||||
else:
|
||||
return HttpResponseRedirect(clist.get_absolute_url())
|
||||
|
@ -162,7 +163,7 @@ def untrack_document(request, name, username=None, acronym=None):
|
|||
if clist.pk is not None:
|
||||
clist.added_docs.remove(doc)
|
||||
|
||||
if request.is_ajax():
|
||||
if is_ajax(request):
|
||||
return HttpResponse(json.dumps({ 'success': True }), content_type='application/json')
|
||||
else:
|
||||
return HttpResponseRedirect(clist.get_absolute_url())
|
||||
|
|
|
@ -11,7 +11,7 @@ from django.utils.html import strip_tags
|
|||
from django.conf import settings
|
||||
from django.urls import reverse as urlreverse
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
from ietf.doc.templatetags.mail_filters import std_level_prompt
|
||||
|
@ -175,7 +175,7 @@ def generate_ballot_writeup(request, doc):
|
|||
e.doc = doc
|
||||
e.rev = doc.rev
|
||||
e.desc = "Ballot writeup was generated"
|
||||
e.text = force_text(render_to_string("doc/mail/ballot_writeup.txt", {'iana': iana, 'doc': doc }))
|
||||
e.text = force_str(render_to_string("doc/mail/ballot_writeup.txt", {'iana': iana, 'doc': doc }))
|
||||
|
||||
# caller is responsible for saving, if necessary
|
||||
return e
|
||||
|
@ -187,7 +187,7 @@ def generate_ballot_rfceditornote(request, doc):
|
|||
e.doc = doc
|
||||
e.rev = doc.rev
|
||||
e.desc = "RFC Editor Note for ballot was generated"
|
||||
e.text = force_text(render_to_string("doc/mail/ballot_rfceditornote.txt"))
|
||||
e.text = force_str(render_to_string("doc/mail/ballot_rfceditornote.txt"))
|
||||
e.save()
|
||||
|
||||
return e
|
||||
|
@ -232,7 +232,7 @@ def generate_last_call_announcement(request, doc):
|
|||
e.doc = doc
|
||||
e.rev = doc.rev
|
||||
e.desc = "Last call announcement was generated"
|
||||
e.text = force_text(mail)
|
||||
e.text = force_str(mail)
|
||||
|
||||
# caller is responsible for saving, if necessary
|
||||
return e
|
||||
|
@ -252,7 +252,7 @@ def generate_approval_mail(request, doc):
|
|||
e.doc = doc
|
||||
e.rev = doc.rev
|
||||
e.desc = "Ballot approval text was generated"
|
||||
e.text = force_text(mail)
|
||||
e.text = force_str(mail)
|
||||
|
||||
# caller is responsible for saving, if necessary
|
||||
return e
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 4.0.10 on 2023-05-16 20:36
|
||||
|
||||
from django.db import migrations
|
||||
import django.db.models.deletion
|
||||
import ietf.utils.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('person', '0001_initial'),
|
||||
('doc', '0003_remove_document_info_order'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='dochistory',
|
||||
name='ad',
|
||||
field=ietf.utils.models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ad_%(class)s_set', to='person.person', verbose_name='area director'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='dochistory',
|
||||
name='shepherd',
|
||||
field=ietf.utils.models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='shepherd_%(class)s_set', to='person.email'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='document',
|
||||
name='ad',
|
||||
field=ietf.utils.models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ad_%(class)s_set', to='person.person', verbose_name='area director'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='document',
|
||||
name='shepherd',
|
||||
field=ietf.utils.models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='shepherd_%(class)s_set', to='person.email'),
|
||||
),
|
||||
]
|
|
@ -24,7 +24,7 @@ from django.urls import reverse as urlreverse
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.html import mark_safe # type:ignore
|
||||
from django.contrib.staticfiles import finders
|
||||
|
||||
|
@ -1134,7 +1134,7 @@ class DocHistory(DocumentInfo):
|
|||
name = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.doc.name)
|
||||
return force_str(self.doc.name)
|
||||
|
||||
def get_related_session(self):
|
||||
return self.doc.get_related_session()
|
||||
|
@ -1196,7 +1196,7 @@ class DocAlias(models.Model):
|
|||
return self.docs.first()
|
||||
|
||||
def __str__(self):
|
||||
return u"%s-->%s" % (self.name, ','.join([force_text(d.name) for d in self.docs.all() if isinstance(d, Document) ]))
|
||||
return u"%s-->%s" % (self.name, ','.join([force_str(d.name) for d in self.docs.all() if isinstance(d, Document) ]))
|
||||
document_link = admin_link("document")
|
||||
class Meta:
|
||||
verbose_name = "document alias"
|
||||
|
|
|
@ -13,8 +13,7 @@ from django.utils.html import escape
|
|||
from django.template.defaultfilters import truncatewords_html, linebreaksbr, stringfilter, striptags
|
||||
from django.utils.safestring import mark_safe, SafeData
|
||||
from django.utils.html import strip_tags
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import force_str # pyflakes:ignore force_str is used in the doctests
|
||||
from django.utils.encoding import force_str
|
||||
from django.urls import reverse as urlreverse
|
||||
from django.core.cache import cache
|
||||
from django.core.exceptions import ValidationError
|
||||
|
@ -132,7 +131,7 @@ register.filter('fill', fill)
|
|||
@register.filter
|
||||
def prettystdname(string, space=" "):
|
||||
from ietf.doc.utils import prettify_std_name
|
||||
return prettify_std_name(force_text(string or ""), space)
|
||||
return prettify_std_name(force_str(string or ""), space)
|
||||
|
||||
@register.filter
|
||||
def rfceditor_info_url(rfcnum : str):
|
||||
|
|
|
@ -1779,7 +1779,7 @@ class DocTestCase(TestCase):
|
|||
self.client.login(username='ad', password='ad+password')
|
||||
r = self.client.post(urlreverse('ietf.doc.views_status_change.change_state',kwargs=dict(name=doc.name)),dict(new_state=iesgeval_pk))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
r = self.client.get(r._headers["location"][1])
|
||||
r = self.client.get(r.headers["location"])
|
||||
self.assertContains(r, ">IESG Evaluation<")
|
||||
self.assertEqual(len(outbox), 2)
|
||||
self.assertIn('iesg-secretary',outbox[0]['To'])
|
||||
|
|
|
@ -573,7 +573,7 @@ class ResurrectTests(DraftFileMixin, TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form [type=submit]')), 1)
|
||||
self.assertEqual(len(q('#content form [type=submit]')), 1)
|
||||
|
||||
|
||||
# request resurrect
|
||||
|
@ -609,7 +609,7 @@ class ResurrectTests(DraftFileMixin, TestCase):
|
|||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('form [type=submit]')), 1)
|
||||
self.assertEqual(len(q('#content form [type=submit]')), 1)
|
||||
|
||||
# complete resurrect
|
||||
events_before = draft.docevent_set.count()
|
||||
|
|
|
@ -118,7 +118,7 @@ class EditAuthorsTests(IetfSeleniumTestCase):
|
|||
# Must provide a "basis" (change reason)
|
||||
self.driver.find_element(By.ID, 'id_basis').send_keys('change testing')
|
||||
# Now click the 'submit' button and check that the update was accepted.
|
||||
submit_button = self.driver.find_element(By.CSS_SELECTOR, 'button[type="submit"]')
|
||||
submit_button = self.driver.find_element(By.CSS_SELECTOR, '#content button[type="submit"]')
|
||||
self.driver.execute_script("arguments[0].click();", submit_button) # FIXME: no idea why this fails:
|
||||
# self.scroll_to_element(submit_button)
|
||||
# submit_button.click()
|
||||
|
|
|
@ -33,9 +33,9 @@
|
|||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
from django.conf.urls import include
|
||||
from django.views.generic import RedirectView
|
||||
from django.conf import settings
|
||||
from django.urls import include
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
from ietf.doc import views_search, views_draft, views_ballot, views_status_change, views_doc, views_downref, views_stats, views_help, views_bofreq
|
||||
from ietf.utils.urls import url
|
||||
|
|
|
@ -17,7 +17,6 @@ from zoneinfo import ZoneInfo
|
|||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.db.models import QuerySet
|
||||
from django.forms import ValidationError
|
||||
from django.http import Http404
|
||||
from django.template.loader import render_to_string
|
||||
|
@ -25,6 +24,7 @@ from django.utils import timezone
|
|||
from django.utils.html import escape
|
||||
from django.urls import reverse as urlreverse
|
||||
|
||||
from django_stubs_ext import QuerySetAny
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
from ietf.community.models import CommunityList
|
||||
|
@ -345,7 +345,7 @@ def augment_events_with_revision(doc, events):
|
|||
"""Take a set of events for doc and add a .rev attribute with the
|
||||
revision they refer to by checking NewRevisionDocEvents."""
|
||||
|
||||
if isinstance(events, QuerySet):
|
||||
if isinstance(events, QuerySetAny):
|
||||
qs = events.filter(newrevisiondocevent__isnull=False)
|
||||
else:
|
||||
qs = NewRevisionDocEvent.objects.filter(doc=doc)
|
||||
|
@ -353,7 +353,7 @@ def augment_events_with_revision(doc, events):
|
|||
|
||||
if doc.type_id == "draft" and doc.get_state_slug() == "rfc":
|
||||
# add fake "RFC" revision
|
||||
if isinstance(events, QuerySet):
|
||||
if isinstance(events, QuerySetAny):
|
||||
e = events.filter(type="published_rfc").order_by('time').last()
|
||||
else:
|
||||
e = doc.latest_event(type="published_rfc")
|
||||
|
|
|
@ -12,7 +12,7 @@ from django.conf import settings
|
|||
from django.urls import reverse as urlreverse
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import smart_text, force_text
|
||||
from django.utils.encoding import smart_str, force_str
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -153,7 +153,7 @@ def generate_ballot_writeup(request, doc):
|
|||
e.doc = doc
|
||||
e.rev = doc.rev,
|
||||
e.desc = "Ballot writeup was generated"
|
||||
e.text = force_text(render_to_string("doc/charter/ballot_writeup.txt"))
|
||||
e.text = force_str(render_to_string("doc/charter/ballot_writeup.txt"))
|
||||
|
||||
# caller is responsible for saving, if necessary
|
||||
return e
|
||||
|
@ -197,7 +197,7 @@ def derive_new_work_text(review_text,group):
|
|||
'Reply_to':'<iesg@ietf.org>'})
|
||||
if not addrs.cc:
|
||||
del m['Cc']
|
||||
return smart_text(m.as_string())
|
||||
return smart_str(m.as_string())
|
||||
|
||||
def default_review_text(group, charter, by):
|
||||
now = timezone.now()
|
||||
|
|
|
@ -17,7 +17,7 @@ from django.conf import settings
|
|||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.html import escape
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
@ -821,7 +821,7 @@ def charter_with_milestones_txt(request, name, rev):
|
|||
|
||||
try:
|
||||
with io.open(os.path.join(settings.CHARTER_PATH, filename), 'r') as f:
|
||||
charter_text = force_text(f.read(), errors='ignore')
|
||||
charter_text = force_str(f.read(), errors='ignore')
|
||||
except IOError:
|
||||
charter_text = "Error reading charter text %s" % filename
|
||||
|
||||
|
|
|
@ -502,6 +502,7 @@ def document_main(request, name, rev=None, document_html=False):
|
|||
html = doc.html_body()
|
||||
if request.COOKIES.get("pagedeps") == "inline":
|
||||
js = Path(finders.find("ietf/js/document_html.js")).read_text()
|
||||
js += Path(finders.find("ietf/js/theme.js")).read_text()
|
||||
css = Path(finders.find("ietf/css/document_html_inline.css")).read_text()
|
||||
if html:
|
||||
css += Path(finders.find("ietf/css/document_html_txt.css")).read_text()
|
||||
|
|
|
@ -11,7 +11,7 @@ import requests
|
|||
import email.utils
|
||||
|
||||
from django.utils import timezone
|
||||
from django.utils.http import is_safe_url
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
|
||||
from simple_history.utils import update_change_reason
|
||||
|
||||
|
@ -53,6 +53,7 @@ from ietf.utils.textupload import get_cleaned_text_file_content
|
|||
from ietf.utils.mail import send_mail_message
|
||||
from ietf.mailtrigger.utils import gather_address_lists
|
||||
from ietf.utils.fields import MultiEmailField
|
||||
from ietf.utils.http import is_ajax
|
||||
from ietf.utils.response import permission_denied
|
||||
from ietf.utils.timezone import date_today, DEADLINE_TZINFO
|
||||
|
||||
|
@ -1087,11 +1088,16 @@ def review_wishes_remove(request, name):
|
|||
|
||||
|
||||
def _generate_ajax_or_redirect_response(request, doc):
|
||||
redirect_url = request.GET.get('next')
|
||||
url_is_safe = is_safe_url(url=redirect_url, allowed_hosts=request.get_host(),
|
||||
require_https=request.is_secure())
|
||||
if request.is_ajax():
|
||||
return HttpResponse(json.dumps({'success': True}), content_type='application/json')
|
||||
redirect_url = request.GET.get("next")
|
||||
url_is_safe = url_has_allowed_host_and_scheme(
|
||||
url=redirect_url,
|
||||
allowed_hosts=request.get_host(),
|
||||
require_https=request.is_secure(),
|
||||
)
|
||||
if is_ajax(request):
|
||||
return HttpResponse(
|
||||
json.dumps({"success": True}), content_type="application/json"
|
||||
)
|
||||
elif url_is_safe:
|
||||
return HttpResponseRedirect(redirect_url)
|
||||
else:
|
||||
|
|
|
@ -765,7 +765,9 @@ def drafts_in_iesg_process(request):
|
|||
if s.slug == "lc":
|
||||
for d in docs:
|
||||
e = d.latest_event(LastCallDocEvent, type="sent_last_call")
|
||||
d.lc_expires = e.expires if e else datetime.datetime.min
|
||||
# If we don't have an event, use an arbitrary date in the past (but not datetime.datetime.min,
|
||||
# which causes problems with timezone conversions)
|
||||
d.lc_expires = e.expires if e else datetime.datetime(1950, 1, 1)
|
||||
docs = list(docs)
|
||||
docs.sort(key=lambda d: d.lc_expires)
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ from django.conf import settings
|
|||
from django.core.cache import cache
|
||||
from django.urls import reverse as urlreverse
|
||||
from django.db.models.aggregates import Count
|
||||
from django.db.models.functions import TruncDate
|
||||
from django.http import JsonResponse, HttpResponseBadRequest
|
||||
from django.shortcuts import render
|
||||
from django.views.decorators.cache import cache_page
|
||||
|
@ -24,7 +25,6 @@ from ietf.utils.timezone import date_today
|
|||
|
||||
epochday = datetime.datetime.utcfromtimestamp(0).date().toordinal()
|
||||
|
||||
column_chart_conf = settings.CHART_TYPE_COLUMN_OPTIONS
|
||||
|
||||
def dt(s):
|
||||
"Convert the date string returned by sqlite's date() to a datetime.date"
|
||||
|
@ -40,15 +40,12 @@ def model_to_timeline_data(model, field='time', **kwargs):
|
|||
assert field in [ f.name for f in model._meta.get_fields() ]
|
||||
|
||||
objects = ( model.objects.filter(**kwargs)
|
||||
.annotate(date=TruncDate(field))
|
||||
.order_by('date')
|
||||
.extra(select={'date': 'date(%s.%s)'% (model._meta.db_table, field) })
|
||||
.values('date')
|
||||
.annotate(count=Count('id')))
|
||||
if objects.exists():
|
||||
obj_list = list(objects)
|
||||
# This is needed for sqlite, when we're running tests:
|
||||
if type(obj_list[0]['date']) != datetime.date:
|
||||
obj_list = [ {'date': dt(e['date']), 'count': e['count']} for e in obj_list ]
|
||||
today = date_today(datetime.timezone.utc)
|
||||
if not obj_list[-1]['date'] == today:
|
||||
obj_list += [ {'date': today, 'count': 0} ]
|
||||
|
|
|
@ -15,7 +15,7 @@ from django.http import Http404, HttpResponseRedirect
|
|||
from django.urls import reverse
|
||||
from django.template.loader import render_to_string
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.html import escape
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
@ -531,7 +531,7 @@ def rfc_status_changes(request):
|
|||
)
|
||||
|
||||
@role_required("Area Director","Secretariat")
|
||||
def start_rfc_status_change(request,name):
|
||||
def start_rfc_status_change(request, name=None):
|
||||
"""Start the RFC status change review process, setting the initial shepherding AD, and possibly putting the review on a telechat."""
|
||||
|
||||
if name:
|
||||
|
@ -665,7 +665,7 @@ def generate_last_call_text(request, doc):
|
|||
e.doc = doc
|
||||
e.rev = doc.rev
|
||||
e.desc = 'Last call announcement was generated'
|
||||
e.text = force_text(new_text)
|
||||
e.text = force_str(new_text)
|
||||
e.save()
|
||||
|
||||
return e
|
||||
|
|
|
@ -14,9 +14,9 @@ from django.contrib.admin.utils import unquote
|
|||
from django.core.management import load_command_class
|
||||
from django.http import Http404
|
||||
from django.shortcuts import render
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.html import escape
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from ietf.group.models import (Group, GroupFeatures, GroupHistory, GroupEvent, GroupURL, GroupMilestone,
|
||||
GroupMilestoneHistory, GroupStateTransitions, Role, RoleHistory, ChangeStateGroupEvent,
|
||||
|
@ -152,7 +152,7 @@ class GroupAdmin(admin.ModelAdmin):
|
|||
permission_denied(request, "You don't have edit permissions for this change.")
|
||||
|
||||
if obj is None:
|
||||
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_text(opts.verbose_name), 'key': escape(object_id)})
|
||||
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_str(opts.verbose_name), 'key': escape(object_id)})
|
||||
|
||||
return self.send_reminder(request, sdo=obj)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ from django import template
|
|||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.group.models import Group
|
||||
from ietf.nomcom.models import NomCom
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
@ -19,14 +19,15 @@ def active_nomcoms(user):
|
|||
if not (user and hasattr(user, "is_authenticated") and user.is_authenticated):
|
||||
return []
|
||||
|
||||
groups = []
|
||||
|
||||
groups.extend(Group.objects.filter(
|
||||
role__person__user=user,
|
||||
type_id='nomcom',
|
||||
state__slug='active').distinct().select_related("type"))
|
||||
|
||||
return groups
|
||||
return list(
|
||||
NomCom.objects.filter(
|
||||
group__role__person__user=user,
|
||||
group__type_id='nomcom', # just in case...
|
||||
group__state__slug='active',
|
||||
)
|
||||
.distinct()
|
||||
.order_by("group__acronym")
|
||||
)
|
||||
|
||||
@register.inclusion_tag('person/person_link.html')
|
||||
def role_person_link(role, **kwargs):
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright The IETF Trust 2013-2020, All Rights Reserved
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include
|
||||
from django.urls import include
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
from ietf.community import views as community_views
|
||||
|
|
|
@ -300,8 +300,27 @@ def active_groups(request, group_type=None):
|
|||
raise Http404
|
||||
|
||||
def active_group_types(request):
|
||||
grouptypes = GroupTypeName.objects.filter(slug__in=['wg','rg','ag','rag','team','dir','review','area','program','iabasg','adm']).filter(group__state='active').annotate(group_count=Count('group'))
|
||||
return render(request, 'group/active_groups.html', {'grouptypes':grouptypes})
|
||||
grouptypes = (
|
||||
GroupTypeName.objects.filter(
|
||||
slug__in=[
|
||||
"wg",
|
||||
"rg",
|
||||
"ag",
|
||||
"rag",
|
||||
"team",
|
||||
"dir",
|
||||
"review",
|
||||
"area",
|
||||
"program",
|
||||
"iabasg",
|
||||
"adm",
|
||||
]
|
||||
)
|
||||
.filter(group__state="active")
|
||||
.order_by('order', 'name') # default ordering ignored for "GROUP BY" queries, make it explicit
|
||||
.annotate(group_count=Count("group"))
|
||||
)
|
||||
return render(request, "group/active_groups.html", {"grouptypes": grouptypes})
|
||||
|
||||
def active_dirs(request):
|
||||
dirs = Group.objects.filter(type__in=['dir', 'review'], state="active").order_by("name")
|
||||
|
|
|
@ -11,14 +11,14 @@ from django.core.exceptions import ValidationError
|
|||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from django_password_strength.widgets import PasswordStrengthInput, PasswordConfirmationInput
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.person.models import Person, Email
|
||||
from ietf.mailinglists.models import Allowlisted
|
||||
from ietf.utils.text import isascii
|
||||
|
||||
from .widgets import PasswordStrengthInput, PasswordConfirmationInput
|
||||
|
||||
|
||||
class RegistrationForm(forms.Form):
|
||||
email = forms.EmailField(label="Your email (lowercase)")
|
||||
|
|
|
@ -98,7 +98,7 @@ class IetfAuthTests(TestCase):
|
|||
self.assertEqual(urlsplit(r["Location"])[2], urlreverse(ietf.ietfauth.views.profile))
|
||||
|
||||
# try logging out
|
||||
r = self.client.get(urlreverse('django.contrib.auth.views.logout'))
|
||||
r = self.client.post(urlreverse('django.contrib.auth.views.logout'), {})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertNotContains(r, "accounts/logout")
|
||||
|
||||
|
@ -215,7 +215,7 @@ class IetfAuthTests(TestCase):
|
|||
self.assertContains(r, "Allowlist entry creation successful")
|
||||
|
||||
# log out
|
||||
r = self.client.get(urlreverse('django.contrib.auth.views.logout'))
|
||||
r = self.client.post(urlreverse('django.contrib.auth.views.logout'), {})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
# register and verify allowlisted email
|
||||
|
@ -664,7 +664,7 @@ class IetfAuthTests(TestCase):
|
|||
"new_password_confirmation": "foobar",
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertFormError(r, 'form', 'current_password', 'Invalid password')
|
||||
self.assertFormError(r.context["form"], 'current_password', 'Invalid password')
|
||||
|
||||
# mismatching new passwords
|
||||
r = self.client.post(chpw_url, {"current_password": "password",
|
||||
|
@ -672,7 +672,7 @@ class IetfAuthTests(TestCase):
|
|||
"new_password_confirmation": "barfoo",
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertFormError(r, 'form', None, "The password confirmation is different than the new password")
|
||||
self.assertFormError(r.context["form"], None, "The password confirmation is different than the new password")
|
||||
|
||||
# correct password change
|
||||
r = self.client.post(chpw_url, {"current_password": "password",
|
||||
|
@ -711,7 +711,7 @@ class IetfAuthTests(TestCase):
|
|||
"password": "password",
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertFormError(r, 'form', 'username',
|
||||
self.assertFormError(r.context["form"], 'username',
|
||||
"Select a valid choice. fiddlesticks is not one of the available choices.")
|
||||
|
||||
# wrong password
|
||||
|
@ -719,7 +719,7 @@ class IetfAuthTests(TestCase):
|
|||
"password": "foobar",
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertFormError(r, 'form', 'password', 'Invalid password')
|
||||
self.assertFormError(r.context["form"], 'password', 'Invalid password')
|
||||
|
||||
# correct username change
|
||||
r = self.client.post(chun_url, {"username": "othername@example.org",
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
import oidc_provider.lib.claims
|
||||
|
||||
|
||||
from functools import wraps
|
||||
from functools import wraps, WRAPPER_ASSIGNMENTS
|
||||
from urllib.parse import quote as urlquote
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
|
@ -15,8 +16,6 @@ from django.core.exceptions import PermissionDenied
|
|||
from django.db.models import Q
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.decorators import available_attrs
|
||||
from django.utils.http import urlquote
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -113,7 +112,7 @@ def passes_test_decorator(test_func, message):
|
|||
error. The test function should be on the form fn(user) ->
|
||||
true/false."""
|
||||
def decorate(view_func):
|
||||
@wraps(view_func, assigned=available_attrs(view_func))
|
||||
@wraps(view_func, assigned=WRAPPER_ASSIGNMENTS)
|
||||
def inner(request, *args, **kwargs):
|
||||
if not request.user.is_authenticated:
|
||||
return HttpResponseRedirect('%s?%s=%s' % (settings.LOGIN_URL, REDIRECT_FIELD_NAME, urlquote(request.get_full_path())))
|
||||
|
|
114
ietf/ietfauth/widgets.py
Normal file
114
ietf/ietfauth/widgets.py
Normal file
|
@ -0,0 +1,114 @@
|
|||
from django.forms import PasswordInput
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
# The PasswordStrengthInput and PasswordConfirmationInput widgets come from the
|
||||
# django-password-strength project, https://pypi.org/project/django-password-strength/
|
||||
#
|
||||
# Original license:
|
||||
#
|
||||
# Copyright © 2015 A.J. May and individual contributors. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
|
||||
# following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
|
||||
# disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
|
||||
# following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
|
||||
# products derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
||||
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
class PasswordStrengthInput(PasswordInput):
|
||||
"""
|
||||
Form widget to show the user how strong his/her password is.
|
||||
"""
|
||||
|
||||
def render(self, name, value, attrs=None, renderer=None):
|
||||
strength_markup = """
|
||||
<div style="margin-top: 10px;">
|
||||
<div class="progress" style="margin-bottom: 10px;">
|
||||
<div class="progress-bar progress-bar-warning password_strength_bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="5" style="width: 0%%"></div>
|
||||
</div>
|
||||
<p class="text-muted password_strength_info hidden">
|
||||
<span class="label label-danger">
|
||||
%s
|
||||
</span>
|
||||
<span style="margin-left:5px;">
|
||||
%s
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
""" % (
|
||||
_("Warning"),
|
||||
_(
|
||||
'This password would take <em class="password_strength_time"></em> to crack.'
|
||||
),
|
||||
)
|
||||
|
||||
try:
|
||||
self.attrs["class"] = "%s password_strength".strip() % self.attrs["class"]
|
||||
except KeyError:
|
||||
self.attrs["class"] = "password_strength"
|
||||
|
||||
return mark_safe(
|
||||
super(PasswordInput, self).render(name, value, attrs, renderer)
|
||||
+ strength_markup
|
||||
)
|
||||
|
||||
class Media:
|
||||
js = (
|
||||
"ietf/js/zxcvbn.js",
|
||||
"ietf/js/password_strength.js",
|
||||
)
|
||||
|
||||
|
||||
class PasswordConfirmationInput(PasswordInput):
|
||||
"""
|
||||
Form widget to confirm the users password by letting him/her type it again.
|
||||
"""
|
||||
|
||||
def __init__(self, confirm_with=None, attrs=None, render_value=False):
|
||||
super(PasswordConfirmationInput, self).__init__(attrs, render_value)
|
||||
self.confirm_with = confirm_with
|
||||
|
||||
def render(self, name, value, attrs=None, renderer=None):
|
||||
if self.confirm_with:
|
||||
self.attrs["data-confirm-with"] = "id_%s" % self.confirm_with
|
||||
|
||||
confirmation_markup = """
|
||||
<div style="margin-top: 10px;" class="hidden password_strength_info">
|
||||
<p class="text-muted">
|
||||
<span class="label label-danger">
|
||||
%s
|
||||
</span>
|
||||
<span style="margin-left:5px;">%s</span>
|
||||
</p>
|
||||
</div>
|
||||
""" % (
|
||||
_("Warning"),
|
||||
_("Your passwords don't match."),
|
||||
)
|
||||
|
||||
try:
|
||||
self.attrs["class"] = (
|
||||
"%s password_confirmation".strip() % self.attrs["class"]
|
||||
)
|
||||
except KeyError:
|
||||
self.attrs["class"] = "password_confirmation"
|
||||
|
||||
return mark_safe(
|
||||
super(PasswordInput, self).render(name, value, attrs, renderer)
|
||||
+ confirmation_markup
|
||||
)
|
|
@ -6,7 +6,7 @@ from django.contrib.syndication.views import Feed
|
|||
from django.utils.feedgenerator import Atom1Feed
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
from ietf.ipr.models import IprDisclosureBase
|
||||
|
||||
|
@ -25,7 +25,7 @@ class LatestIprDisclosuresFeed(Feed):
|
|||
return mark_safe(item.title)
|
||||
|
||||
def item_description(self, item):
|
||||
return force_text(item.title)
|
||||
return force_str(item.title)
|
||||
|
||||
def item_pubdate(self, item):
|
||||
return item.time
|
||||
|
|
|
@ -12,7 +12,7 @@ from email import message_from_bytes
|
|||
from email.utils import parsedate_tz
|
||||
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.encoding import force_text, force_bytes
|
||||
from django.utils.encoding import force_str, force_bytes
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -102,7 +102,7 @@ def get_reply_to():
|
|||
address with "plus addressing" using a random string. Guaranteed to be unique"""
|
||||
local,domain = get_base_ipr_request_address().split('@')
|
||||
while True:
|
||||
rand = force_text(base64.urlsafe_b64encode(os.urandom(12)))
|
||||
rand = force_str(base64.urlsafe_b64encode(os.urandom(12)))
|
||||
address = "{}+{}@{}".format(local,rand,domain)
|
||||
q = Message.objects.filter(reply_to=address)
|
||||
if not q:
|
||||
|
|
|
@ -9,16 +9,15 @@ import operator
|
|||
from typing import Union # pyflakes:ignore
|
||||
|
||||
from email.utils import parseaddr
|
||||
from form_utils.forms import BetterModelForm
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||
from django.db.models.query import QuerySet
|
||||
from django.forms.utils import ErrorList
|
||||
from django.db.models import Q
|
||||
#from django.forms.widgets import RadioFieldRenderer
|
||||
from django.core.validators import validate_email
|
||||
from django_stubs_ext import QuerySetAny
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -132,7 +131,7 @@ class AddCommentForm(forms.Form):
|
|||
# def render(self):
|
||||
# output = []
|
||||
# for widget in self:
|
||||
# output.append(format_html(force_text(widget)))
|
||||
# output.append(format_html(force_str(widget)))
|
||||
# return mark_safe('\n'.join(output))
|
||||
|
||||
|
||||
|
@ -204,7 +203,7 @@ class SearchLiaisonForm(forms.Form):
|
|||
class CustomModelMultipleChoiceField(forms.ModelMultipleChoiceField):
|
||||
'''If value is a QuerySet, return it as is (for use in widget.render)'''
|
||||
def prepare_value(self, value):
|
||||
if isinstance(value, QuerySet):
|
||||
if isinstance(value, QuerySetAny):
|
||||
return value
|
||||
if (hasattr(value, '__iter__') and
|
||||
not isinstance(value, str) and
|
||||
|
@ -213,7 +212,7 @@ class CustomModelMultipleChoiceField(forms.ModelMultipleChoiceField):
|
|||
return super(CustomModelMultipleChoiceField, self).prepare_value(value)
|
||||
|
||||
|
||||
class LiaisonModelForm(BetterModelForm):
|
||||
class LiaisonModelForm(forms.ModelForm):
|
||||
'''Specify fields which require a custom widget or that are not part of the model.
|
||||
'''
|
||||
from_groups = forms.ModelMultipleChoiceField(queryset=Group.objects.all(),label='Groups',required=False)
|
||||
|
@ -238,13 +237,6 @@ class LiaisonModelForm(BetterModelForm):
|
|||
class Meta:
|
||||
model = LiaisonStatement
|
||||
exclude = ('attachments','state','from_name','to_name')
|
||||
fieldsets = [('From', {'fields': ['from_groups','from_contact', 'response_contacts'], 'legend': ''}),
|
||||
('To', {'fields': ['to_groups','to_contacts'], 'legend': ''}),
|
||||
('Other email addresses', {'fields': ['technical_contacts','action_holder_contacts','cc_contacts'], 'legend': ''}),
|
||||
('Purpose', {'fields':['purpose', 'deadline'], 'legend': ''}),
|
||||
('Reference', {'fields': ['other_identifiers','related_to'], 'legend': ''}),
|
||||
('Liaison Statement', {'fields': ['title', 'submitted_date', 'body', 'attachments'], 'legend': ''}),
|
||||
('Add attachment', {'fields': ['attach_title', 'attach_file', 'attach_button'], 'legend': ''})]
|
||||
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
super(LiaisonModelForm, self).__init__(*args, **kwargs)
|
||||
|
@ -476,14 +468,6 @@ class OutgoingLiaisonForm(LiaisonModelForm):
|
|||
class Meta:
|
||||
model = LiaisonStatement
|
||||
exclude = ('attachments','state','from_name','to_name','action_holder_contacts')
|
||||
# add approved field, no action_holder_contacts
|
||||
fieldsets = [('From', {'fields': ['from_groups','from_contact','response_contacts','approved'], 'legend': ''}),
|
||||
('To', {'fields': ['to_groups','to_contacts'], 'legend': ''}),
|
||||
('Other email addresses', {'fields': ['technical_contacts','cc_contacts'], 'legend': ''}),
|
||||
('Purpose', {'fields':['purpose', 'deadline'], 'legend': ''}),
|
||||
('Reference', {'fields': ['other_identifiers','related_to'], 'legend': ''}),
|
||||
('Liaison Statement', {'fields': ['title', 'submitted_date', 'body', 'attachments'], 'legend': ''}),
|
||||
('Add attachment', {'fields': ['attach_title', 'attach_file', 'attach_button'], 'legend': ''})]
|
||||
|
||||
def is_approved(self):
|
||||
return self.cleaned_data['approved']
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
|
||||
|
||||
from django.urls import reverse as urlreverse
|
||||
from django.db.models.query import QuerySet
|
||||
from django.forms.widgets import Widget
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.html import conditional_escape
|
||||
|
||||
from django_stubs_ext import QuerySetAny
|
||||
|
||||
|
||||
class ButtonWidget(Widget):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -34,7 +35,7 @@ class ShowAttachmentsWidget(Widget):
|
|||
html = '<div id="id_%s">' % name
|
||||
html += '<span class="d-none showAttachmentsEmpty form-control widget">No files attached</span>'
|
||||
html += '<div class="attachedFiles form-control widget">'
|
||||
if value and isinstance(value, QuerySet):
|
||||
if value and isinstance(value, QuerySetAny):
|
||||
for attachment in value:
|
||||
html += '<a class="initialAttach" href="%s">%s</a> ' % (conditional_escape(attachment.document.get_href()), conditional_escape(attachment.document.title))
|
||||
html += '<a class="btn btn-primary btn-sm" href="{}">Edit</a> '.format(urlreverse("ietf.liaisons.views.liaison_edit_attachment", kwargs={'object_id':attachment.statement.pk,'doc_id':attachment.document.pk}))
|
||||
|
|
|
@ -7,9 +7,7 @@ import datetime
|
|||
import shutil
|
||||
import os
|
||||
import re
|
||||
from unittest import skipIf
|
||||
|
||||
import django
|
||||
from django.utils import timezone
|
||||
from django.utils.text import slugify
|
||||
from django.db.models import F
|
||||
|
@ -880,42 +878,6 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase):
|
|||
self.assertNotIn('would-violate-hint', session_elements[4].get_attribute('class'),
|
||||
'Constraint violation should not be indicated on non-conflicting session')
|
||||
|
||||
@ifSeleniumEnabled
|
||||
@skipIf(django.VERSION[0]==2, "Skipping test with race conditions under Django 2")
|
||||
class ScheduleEditTests(IetfSeleniumTestCase):
|
||||
def testUnschedule(self):
|
||||
|
||||
meeting = make_meeting_test_data()
|
||||
|
||||
self.assertEqual(SchedTimeSessAssignment.objects.filter(session__meeting=meeting, session__group__acronym='mars', schedule__name='test-schedule').count(),1)
|
||||
|
||||
|
||||
ss = list(SchedTimeSessAssignment.objects.filter(session__meeting__number=72,session__group__acronym='mars',schedule__name='test-schedule')) # pyflakes:ignore
|
||||
|
||||
self.login()
|
||||
url = self.absreverse('ietf.meeting.views.edit_meeting_schedule',kwargs=dict(num='72',name='test-schedule',owner='plain@example.com'))
|
||||
self.driver.get(url)
|
||||
|
||||
# driver.get() will wait for scripts to finish, but not ajax
|
||||
# requests. Wait for completion of the permissions check:
|
||||
read_only_note = self.driver.find_element(By.ID, 'read_only')
|
||||
WebDriverWait(self.driver, 10).until(expected_conditions.invisibility_of_element(read_only_note), "Read-only schedule")
|
||||
|
||||
s1 = Session.objects.filter(group__acronym='mars', meeting=meeting).first()
|
||||
selector = "#session_{}".format(s1.pk)
|
||||
WebDriverWait(self.driver, 30).until(expected_conditions.presence_of_element_located((By.CSS_SELECTOR, selector)), "Did not find %s"%selector)
|
||||
|
||||
self.assertEqual(self.driver.find_elements(By.CSS_SELECTOR, "#sortable-list #session_{}".format(s1.pk)), [])
|
||||
|
||||
element = self.driver.find_element(By.ID, 'session_{}'.format(s1.pk))
|
||||
target = self.driver.find_element(By.ID, 'sortable-list')
|
||||
ActionChains(self.driver).drag_and_drop(element,target).perform()
|
||||
|
||||
self.assertTrue(self.driver.find_elements(By.CSS_SELECTOR, "#sortable-list #session_{}".format(s1.pk)))
|
||||
|
||||
time.sleep(0.1) # The API that modifies the database runs async
|
||||
|
||||
self.assertEqual(SchedTimeSessAssignment.objects.filter(session__meeting__number=72,session__group__acronym='mars',schedule__name='test-schedule').count(),0)
|
||||
|
||||
@ifSeleniumEnabled
|
||||
class SlideReorderTests(IetfSeleniumTestCase):
|
||||
|
@ -1531,7 +1493,7 @@ class EditTimeslotsTests(IetfSeleniumTestCase):
|
|||
"""Test the timeslot editor"""
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.meeting: Meeting = MeetingFactory(
|
||||
self.meeting: Meeting = MeetingFactory( # type: ignore[annotation-unchecked]
|
||||
type_id='ietf',
|
||||
number=120,
|
||||
date=date_today() + datetime.timedelta(days=10),
|
||||
|
@ -1608,13 +1570,13 @@ class EditTimeslotsTests(IetfSeleniumTestCase):
|
|||
delete_time = delete_time_local.astimezone(datetime.timezone.utc)
|
||||
duration = datetime.timedelta(minutes=60)
|
||||
|
||||
delete: [TimeSlot] = TimeSlotFactory.create_batch(
|
||||
delete: [TimeSlot] = TimeSlotFactory.create_batch( # type: ignore[annotation-unchecked]
|
||||
2,
|
||||
meeting=self.meeting,
|
||||
time=delete_time_local,
|
||||
duration=duration,
|
||||
)
|
||||
keep: [TimeSlot] = [
|
||||
keep: [TimeSlot] = [ # type: ignore[annotation-unchecked]
|
||||
TimeSlotFactory(
|
||||
meeting=self.meeting,
|
||||
time=keep_time,
|
||||
|
@ -1651,14 +1613,14 @@ class EditTimeslotsTests(IetfSeleniumTestCase):
|
|||
hours = [10, 12]
|
||||
other_days = [self.meeting.get_meeting_date(d) for d in range(1, 3)]
|
||||
|
||||
delete: [TimeSlot] = [
|
||||
delete: [TimeSlot] = [ # type: ignore[annotation-unchecked]
|
||||
TimeSlotFactory(
|
||||
meeting=self.meeting,
|
||||
time=datetime_from_date(delete_day, self.meeting.tz()).replace(hour=hour),
|
||||
) for hour in hours
|
||||
]
|
||||
|
||||
keep: [TimeSlot] = [
|
||||
keep: [TimeSlot] = [ # type: ignore[annotation-unchecked]
|
||||
TimeSlotFactory(
|
||||
meeting=self.meeting,
|
||||
time=datetime_from_date(day, self.meeting.tz()).replace(hour=hour),
|
||||
|
|
|
@ -556,7 +556,7 @@ class MeetingTests(BaseMeetingTestCase):
|
|||
self.assertContains(r, "1. More work items underway")
|
||||
|
||||
|
||||
cont_disp = r._headers.get('content-disposition', ('Content-Disposition', ''))[1]
|
||||
cont_disp = r.headers.get('content-disposition', ('Content-Disposition', ''))[1]
|
||||
cont_disp = re.split('; ?', cont_disp)
|
||||
cont_disp_settings = dict( e.split('=', 1) for e in cont_disp if '=' in e )
|
||||
filename = cont_disp_settings.get('filename', '').strip('"')
|
||||
|
@ -2357,7 +2357,7 @@ class EditTimeslotsTests(TestCase):
|
|||
|
||||
def test_invalid_edit_timeslot(self):
|
||||
meeting = self.create_bare_meeting()
|
||||
ts: TimeSlot = TimeSlotFactory(meeting=meeting, name='slot') # n.b., colon indicates type hinting
|
||||
ts: TimeSlot = TimeSlotFactory(meeting=meeting, name='slot') # type: ignore[annotation-unchecked]
|
||||
self.login()
|
||||
r = self.client.post(
|
||||
self.edit_timeslot_url(ts),
|
||||
|
@ -3931,7 +3931,7 @@ class EditTests(TestCase):
|
|||
'base': meeting.schedule.base_id,
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertFormError(r, 'form', 'name', 'Enter a valid value.')
|
||||
self.assertFormError(r.context["form"], 'name', 'Enter a valid value.')
|
||||
self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created')
|
||||
|
||||
r = self.client.post(url, {
|
||||
|
@ -3941,7 +3941,7 @@ class EditTests(TestCase):
|
|||
'base': meeting.schedule.base_id,
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertFormError(r, 'form', 'name', 'Enter a valid value.')
|
||||
self.assertFormError(r.context["form"], 'name', 'Enter a valid value.')
|
||||
self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created')
|
||||
|
||||
# Non-ASCII alphanumeric characters
|
||||
|
@ -3952,7 +3952,7 @@ class EditTests(TestCase):
|
|||
'base': meeting.schedule.base_id,
|
||||
})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertFormError(r, 'form', 'name', 'Enter a valid value.')
|
||||
self.assertFormError(r.context["form"], 'name', 'Enter a valid value.')
|
||||
self.assertEqual(meeting.schedule_set.count(), orig_schedule_count, 'Schedule should not be created')
|
||||
|
||||
def test_edit_session(self):
|
||||
|
@ -4039,9 +4039,9 @@ class EditTests(TestCase):
|
|||
self.assertIn(return_url_unofficial, r.content.decode())
|
||||
|
||||
r = self.client.post(url, {})
|
||||
self.assertFormError(r, 'form', 'confirmed', 'This field is required.')
|
||||
self.assertFormError(r.context["form"], 'confirmed', 'This field is required.')
|
||||
r = self.client.post(url_unofficial, {})
|
||||
self.assertFormError(r, 'form', 'confirmed', 'This field is required.')
|
||||
self.assertFormError(r.context["form"], 'confirmed', 'This field is required.')
|
||||
|
||||
r = self.client.post(url, {'confirmed': 'on'})
|
||||
self.assertRedirects(r, return_url)
|
||||
|
@ -6520,8 +6520,8 @@ class ImportNotesTests(TestCase):
|
|||
r = self.client.get(url) # try to import the same text
|
||||
self.assertContains(r, "This document is identical", status_code=200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('button:disabled[type="submit"]')), 1)
|
||||
self.assertEqual(len(q('button:enabled[type="submit"]')), 0)
|
||||
self.assertEqual(len(q('#content button:disabled[type="submit"]')), 1)
|
||||
self.assertEqual(len(q('#content button:enabled[type="submit"]')), 0)
|
||||
|
||||
def test_allows_import_on_existing_bad_unicode(self):
|
||||
"""Should not be able to import text identical to the current revision"""
|
||||
|
@ -6545,8 +6545,8 @@ class ImportNotesTests(TestCase):
|
|||
r = self.client.get(url) # try to import the same text
|
||||
self.assertNotContains(r, "This document is identical", status_code=200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('button:enabled[type="submit"]')), 1)
|
||||
self.assertEqual(len(q('button:disabled[type="submit"]')), 0)
|
||||
self.assertEqual(len(q('#content button:enabled[type="submit"]')), 1)
|
||||
self.assertEqual(len(q('#content button:disabled[type="submit"]')), 0)
|
||||
|
||||
def test_handles_missing_previous_revision_file(self):
|
||||
"""Should still allow import if the file for the previous revision is missing"""
|
||||
|
@ -7975,7 +7975,7 @@ class ProceedingsTests(BaseMeetingTestCase):
|
|||
invalid_file.seek(0) # read the file contents again
|
||||
r = self.client.post(url, {'file': invalid_file, 'external_url': ''})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertFormError(r, 'form', 'file', 'Found an unexpected extension: .png. Expected one of .pdf')
|
||||
self.assertFormError(r.context["form"], 'file', 'Found an unexpected extension: .png. Expected one of .pdf')
|
||||
|
||||
def test_add_proceedings_material_doc_empty(self):
|
||||
"""Upload proceedings materials document without specifying a file"""
|
||||
|
@ -7988,7 +7988,7 @@ class ProceedingsTests(BaseMeetingTestCase):
|
|||
)
|
||||
r = self.client.post(url, {'external_url': ''})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertFormError(r, 'form', 'file', 'This field is required')
|
||||
self.assertFormError(r.context["form"], 'file', 'This field is required')
|
||||
|
||||
def test_add_proceedings_material_url(self):
|
||||
"""Add a URL as proceedings material"""
|
||||
|
@ -8012,7 +8012,7 @@ class ProceedingsTests(BaseMeetingTestCase):
|
|||
)
|
||||
r = self.client.post(url, {'use_url': 'on', 'external_url': "Ceci n'est pas une URL"})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertFormError(r, 'form', 'external_url', 'Enter a valid URL.')
|
||||
self.assertFormError(r.context["form"], 'external_url', 'Enter a valid URL.')
|
||||
|
||||
def test_add_proceedings_material_url_empty(self):
|
||||
"""Add proceedings materials URL without specifying the URL"""
|
||||
|
@ -8025,7 +8025,7 @@ class ProceedingsTests(BaseMeetingTestCase):
|
|||
)
|
||||
r = self.client.post(url, {'use_url': 'on', 'external_url': ''})
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertFormError(r, 'form', 'external_url', 'This field is required')
|
||||
self.assertFormError(r.context["form"], 'external_url', 'This field is required')
|
||||
|
||||
@override_settings(MEETING_DOC_HREFS={'procmaterials': '{doc.name}:{doc.rev}'})
|
||||
def test_replace_proceedings_material(self):
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# Copyright The IETF Trust 2007-2020, All Rights Reserved
|
||||
|
||||
from django.conf.urls import include
|
||||
from django.views.generic import RedirectView
|
||||
from django.conf import settings
|
||||
from django.urls import include
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
from ietf.meeting import views, views_proceedings
|
||||
from ietf.utils.urls import url
|
||||
|
|
|
@ -15,7 +15,7 @@ from django.conf import settings
|
|||
from django.contrib import messages
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import smart_text
|
||||
from django.utils.encoding import smart_str
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -699,7 +699,7 @@ def handle_upload_file(file, filename, meeting, subdir, request=None, encoding=N
|
|||
)
|
||||
else:
|
||||
try:
|
||||
text = smart_text(text)
|
||||
text = smart_str(text)
|
||||
except UnicodeDecodeError as e:
|
||||
return "Failure trying to save '%s'. Hint: Try to upload as UTF-8: %s..." % (filename, str(e)[:120])
|
||||
# Whole file sanitization; add back what's missing from a complete
|
||||
|
|
|
@ -17,6 +17,7 @@ import tempfile
|
|||
|
||||
from calendar import timegm
|
||||
from collections import OrderedDict, Counter, deque, defaultdict, namedtuple
|
||||
from functools import partialmethod
|
||||
from urllib.parse import parse_qs, unquote, urlencode, urlsplit, urlunsplit
|
||||
from tempfile import mkstemp
|
||||
from wsgiref.handlers import format_date_time
|
||||
|
@ -38,7 +39,6 @@ from django.template import TemplateDoesNotExist
|
|||
from django.template.loader import render_to_string
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.functional import curry
|
||||
from django.utils.text import slugify
|
||||
from django.views.decorators.cache import cache_page
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt
|
||||
|
@ -2730,7 +2730,7 @@ def upload_session_agenda(request, session_id, num):
|
|||
})
|
||||
|
||||
|
||||
def upload_session_slides(request, session_id, num, name):
|
||||
def upload_session_slides(request, session_id, num, name=None):
|
||||
# num is redundant, but we're dragging it along an artifact of where we are in the current URL structure
|
||||
session = get_object_or_404(Session,pk=session_id)
|
||||
if not session.can_manage_materials(request.user):
|
||||
|
@ -3210,8 +3210,8 @@ def interim_request(request):
|
|||
if meeting_type in ('single', 'multi-day'):
|
||||
meeting = form.save(date=get_earliest_session_date(formset))
|
||||
|
||||
# need to use curry here to pass custom variable to form init
|
||||
SessionFormset.form.__init__ = curry(
|
||||
# need to use partialmethod here to pass custom variable to form init
|
||||
SessionFormset.form.__init__ = partialmethod(
|
||||
InterimSessionModelForm.__init__,
|
||||
user=request.user,
|
||||
group=group,
|
||||
|
@ -3233,7 +3233,7 @@ def interim_request(request):
|
|||
# subsequently dealt with individually
|
||||
elif meeting_type == 'series':
|
||||
series = []
|
||||
SessionFormset.form.__init__ = curry(
|
||||
SessionFormset.form.__init__ = partialmethod(
|
||||
InterimSessionModelForm.__init__,
|
||||
user=request.user,
|
||||
group=group,
|
||||
|
@ -3453,7 +3453,7 @@ def interim_request_edit(request, number):
|
|||
group = Group.objects.get(pk=form.data['group'])
|
||||
is_approved = is_interim_meeting_approved(meeting)
|
||||
|
||||
SessionFormset.form.__init__ = curry(
|
||||
SessionFormset.form.__init__ = partialmethod(
|
||||
InterimSessionModelForm.__init__,
|
||||
user=request.user,
|
||||
group=group,
|
||||
|
|
|
@ -67,7 +67,7 @@ class Command(BaseCommand):
|
|||
pprint(connection.queries)
|
||||
raise
|
||||
|
||||
objects = [] # type: List[object]
|
||||
objects: List[object] = [] # type: ignore[annotation-unchecked]
|
||||
model_objects = {}
|
||||
|
||||
import ietf.name.models
|
||||
|
|
|
@ -3,10 +3,11 @@
|
|||
|
||||
|
||||
import functools
|
||||
from urllib.parse import quote as urlquote
|
||||
|
||||
from django.urls import reverse
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.utils.http import urlquote
|
||||
|
||||
|
||||
|
||||
def nomcom_private_key_required(view_func):
|
||||
|
|
|
@ -66,7 +66,7 @@ Tdb0MiLc+r/zvx8oXtgDjDUa
|
|||
|
||||
def provide_private_key_to_test_client(testcase):
|
||||
session = testcase.client.session
|
||||
session['NOMCOM_PRIVATE_KEY_%s'%testcase.nc.year()] = key
|
||||
session['NOMCOM_PRIVATE_KEY_%s'%testcase.nc.year()] = key.decode("utf8")
|
||||
session.save()
|
||||
|
||||
def nomcom_kwargs_for_year(year=None, *args, **kwargs):
|
||||
|
|
|
@ -188,8 +188,9 @@ class NomineePosition(models.Model):
|
|||
|
||||
def save(self, **kwargs):
|
||||
if not self.pk and not self.state_id:
|
||||
# Don't need to set update_fields because the self.pk test means this is a new instance
|
||||
self.state = NomineePositionStateName.objects.get(slug='pending')
|
||||
super(NomineePosition, self).save(**kwargs)
|
||||
super().save(**kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return "%s - %s - %s" % (self.nominee, self.state, self.position)
|
||||
|
|
|
@ -6,7 +6,7 @@ import re
|
|||
from django import template
|
||||
from django.conf import settings
|
||||
from django.template.defaultfilters import linebreaksbr, force_escape
|
||||
from django.utils.encoding import force_text, DjangoUnicodeDecodeError
|
||||
from django.utils.encoding import force_str, DjangoUnicodeDecodeError
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
@ -55,8 +55,10 @@ def formatted_email(address):
|
|||
|
||||
@register.simple_tag
|
||||
def decrypt(string, request, year, plain=False):
|
||||
try:
|
||||
key = retrieve_nomcom_private_key(request, year)
|
||||
|
||||
except UnicodeError:
|
||||
return f"-*- Encrypted text [Error retrieving private key, contact the secretariat ({settings.SECRETARIAT_SUPPORT_EMAIL})]"
|
||||
if not key:
|
||||
return '-*- Encrypted text [No private key provided] -*-'
|
||||
|
||||
|
@ -68,7 +70,7 @@ def decrypt(string, request, year, plain=False):
|
|||
code, out, error = pipe(command % (settings.OPENSSL_COMMAND,
|
||||
encrypted_file.name), key)
|
||||
try:
|
||||
out = force_text(out)
|
||||
out = force_str(out)
|
||||
except DjangoUnicodeDecodeError:
|
||||
pass
|
||||
if code != 0:
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import datetime
|
||||
import io
|
||||
import mock
|
||||
import random
|
||||
import shutil
|
||||
|
||||
|
@ -1577,6 +1578,16 @@ class NewActiveNomComTests(TestCase):
|
|||
login_testing_unauthorized(self,self.chair.user.username,url)
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code,200)
|
||||
# Check that we get an error if there's an encoding problem talking to openssl
|
||||
# "\xc3\x28" is an invalid utf8 string
|
||||
with mock.patch("ietf.nomcom.utils.pipe", return_value=(0, b"\xc3\x28", None)):
|
||||
response = self.client.post(url, {'key': force_str(key)})
|
||||
self.assertFormError(
|
||||
response.context["form"],
|
||||
None,
|
||||
"An internal error occurred while adding your private key to your session."
|
||||
f"Please contact the secretariat for assistance ({settings.SECRETARIAT_SUPPORT_EMAIL})",
|
||||
)
|
||||
response = self.client.post(url,{'key': force_str(key)})
|
||||
self.assertEqual(response.status_code,302)
|
||||
|
||||
|
@ -1993,7 +2004,7 @@ class NoPublicKeyTests(TestCase):
|
|||
text_bits = [x.xpath('.//text()') for x in q('.alert-warning')]
|
||||
flat_text_bits = [item for sublist in text_bits for item in sublist]
|
||||
self.assertTrue(any(['not yet' in y for y in flat_text_bits]))
|
||||
self.assertEqual(bool(q('form:not(.navbar-form)')),expected_form)
|
||||
self.assertEqual(bool(q('#content form:not(.navbar-form)')),expected_form)
|
||||
self.client.logout()
|
||||
|
||||
def test_not_yet(self):
|
||||
|
|
|
@ -172,6 +172,12 @@ def command_line_safe_secret(secret):
|
|||
return base64.encodebytes(secret).decode('utf-8').rstrip()
|
||||
|
||||
def retrieve_nomcom_private_key(request, year):
|
||||
"""Retrieve decrypted nomcom private key from the session store
|
||||
|
||||
Retrieves encrypted, ascii-armored private key from the session store, encodes
|
||||
as utf8 bytes, then decrypts. Raises UnicodeError if the value in the session
|
||||
store cannot be encoded as utf8.
|
||||
"""
|
||||
private_key = request.session.get('NOMCOM_PRIVATE_KEY_%s' % year, None)
|
||||
|
||||
if not private_key:
|
||||
|
@ -183,7 +189,8 @@ def retrieve_nomcom_private_key(request, year):
|
|||
settings.OPENSSL_COMMAND,
|
||||
command_line_safe_secret(settings.NOMCOM_APP_SECRET)
|
||||
),
|
||||
private_key
|
||||
# The openssl command expects ascii-armored input, so utf8 encoding should be valid
|
||||
private_key.encode("utf8")
|
||||
)
|
||||
if code != 0:
|
||||
log("openssl error: %s:\n Error %s: %s" %(command, code, error))
|
||||
|
@ -191,6 +198,12 @@ def retrieve_nomcom_private_key(request, year):
|
|||
|
||||
|
||||
def store_nomcom_private_key(request, year, private_key):
|
||||
"""Put encrypted nomcom private key in the session store
|
||||
|
||||
Encrypts the private key using openssl, then decodes the ascii-armored output
|
||||
as utf8 and adds to the session store. Raises UnicodeError if the openssl's
|
||||
output cannot be decoded as utf8.
|
||||
"""
|
||||
if not private_key:
|
||||
request.session['NOMCOM_PRIVATE_KEY_%s' % year] = ''
|
||||
else:
|
||||
|
@ -205,8 +218,9 @@ def store_nomcom_private_key(request, year, private_key):
|
|||
if code != 0:
|
||||
log("openssl error: %s:\n Error %s: %s" %(command, code, error))
|
||||
if error and error!=b"*** WARNING : deprecated key derivation used.\nUsing -iter or -pbkdf2 would be better.\n":
|
||||
out = ''
|
||||
request.session['NOMCOM_PRIVATE_KEY_%s' % year] = out
|
||||
out = b''
|
||||
# The openssl command output in 'out' is an ascii-armored value, so should be utf8-decodable
|
||||
request.session['NOMCOM_PRIVATE_KEY_%s' % year] = out.decode("utf8")
|
||||
|
||||
|
||||
def validate_private_key(key):
|
||||
|
|
|
@ -18,7 +18,7 @@ from django.http import Http404, HttpResponseRedirect, HttpResponse
|
|||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
from django.utils.encoding import force_bytes, force_str
|
||||
|
||||
|
||||
from ietf.dbtemplate.models import DBTemplate
|
||||
|
@ -158,7 +158,15 @@ def private_key(request, year):
|
|||
if request.method == 'POST':
|
||||
form = PrivateKeyForm(data=request.POST)
|
||||
if form.is_valid():
|
||||
try:
|
||||
store_nomcom_private_key(request, year, force_bytes(form.cleaned_data.get('key', '')))
|
||||
except UnicodeError:
|
||||
form.add_error(
|
||||
None,
|
||||
"An internal error occurred while adding your private key to your session."
|
||||
f"Please contact the secretariat for assistance ({settings.SECRETARIAT_SUPPORT_EMAIL})"
|
||||
)
|
||||
else:
|
||||
return HttpResponseRedirect(back_url)
|
||||
else:
|
||||
form = PrivateKeyForm()
|
||||
|
@ -684,7 +692,7 @@ def private_questionnaire(request, year):
|
|||
if form.is_valid():
|
||||
form.save()
|
||||
messages.success(request, 'The questionnaire response has been registered.')
|
||||
questionnaire_response = force_text(form.cleaned_data['comment_text'])
|
||||
questionnaire_response = force_str(form.cleaned_data['comment_text'])
|
||||
form = QuestionnaireForm(nomcom=nomcom, user=request.user)
|
||||
else:
|
||||
form = QuestionnaireForm(nomcom=nomcom, user=request.user)
|
||||
|
|
|
@ -16,7 +16,7 @@ from unicodedata import normalize
|
|||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.text import slugify
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -68,7 +68,7 @@ class PersonFactory(factory.django.DjangoModelFactory):
|
|||
# Some i18n names, e.g., "शिला के.सी." have a dot at the end that is also part of the ASCII, e.g., "Shilaa Kesii."
|
||||
# That trailing dot breaks extract_authors(). Avoid this issue by stripping the dot from the ASCII.
|
||||
# Some others have a trailing semicolon (e.g., "உயிரோவியம் தங்கராஐ;") - strip those, too.
|
||||
ascii = factory.LazyAttribute(lambda p: force_text(unidecode_name(p.name)).rstrip(".;"))
|
||||
ascii = factory.LazyAttribute(lambda p: force_str(unidecode_name(p.name)).rstrip(".;"))
|
||||
|
||||
class Params:
|
||||
with_bio = factory.Trait(biography = "\n\n".join(fake.paragraphs())) # type: ignore
|
||||
|
|
|
@ -183,6 +183,9 @@ class AbstractReviewerQueuePolicy:
|
|||
role__group=review_req.team
|
||||
).exclude( person_id__in=rejecting_reviewer_ids )
|
||||
|
||||
one_assignment = None
|
||||
if review_req.pk is not None:
|
||||
# cannot use reviewassignment_set relation until review_req has been created
|
||||
one_assignment = (review_req.reviewassignment_set
|
||||
.exclude(state__slug__in=('rejected', 'no-response'))
|
||||
.first())
|
||||
|
|
|
@ -382,7 +382,8 @@ def assign_review_request_to_reviewer(request, review_req, reviewer, add_skip=Fa
|
|||
# with a different view on a ReviewAssignment.
|
||||
log.assertion('reviewer is not None')
|
||||
|
||||
if review_req.reviewassignment_set.filter(reviewer=reviewer).exists():
|
||||
# cannot reference reviewassignment_set relation until pk exists
|
||||
if review_req.pk is not None and review_req.reviewassignment_set.filter(reviewer=reviewer).exists():
|
||||
return
|
||||
|
||||
# Note that assigning a review no longer unassigns other reviews
|
||||
|
@ -598,7 +599,9 @@ def suggested_review_requests_for_team(team):
|
|||
res.sort(key=lambda r: (r.deadline, r.doc_id), reverse=True)
|
||||
return res
|
||||
|
||||
def extract_revision_ordered_review_assignments_for_documents_and_replaced(review_assignment_queryset, names):
|
||||
def extract_revision_ordered_review_assignments_for_documents_and_replaced(
|
||||
review_assignment_queryset, names
|
||||
):
|
||||
"""Extracts all review assignments for document names (including replaced ancestors), return them neatly sorted."""
|
||||
|
||||
names = set(names)
|
||||
|
@ -607,8 +610,13 @@ def extract_revision_ordered_review_assignments_for_documents_and_replaced(revie
|
|||
|
||||
assignments_for_each_doc = defaultdict(list)
|
||||
replacement_name_set = set(e for l in replaces.values() for e in l) | names
|
||||
for r in ( review_assignment_queryset.filter(review_request__doc__name__in=replacement_name_set)
|
||||
.order_by("-reviewed_rev","-assigned_on", "-id").iterator()):
|
||||
for r in (
|
||||
review_assignment_queryset.filter(
|
||||
review_request__doc__name__in=replacement_name_set
|
||||
)
|
||||
.order_by("-reviewed_rev", "-assigned_on", "-id")
|
||||
.iterator(chunk_size=2000) # chunk_size not tested, using pre-Django 5 default value
|
||||
):
|
||||
assignments_for_each_doc[r.review_request.doc.name].append(r)
|
||||
|
||||
# now collect in breadth-first order to keep the revision order intact
|
||||
|
@ -646,7 +654,10 @@ def extract_revision_ordered_review_assignments_for_documents_and_replaced(revie
|
|||
|
||||
return res
|
||||
|
||||
def extract_revision_ordered_review_requests_for_documents_and_replaced(review_request_queryset, names):
|
||||
|
||||
def extract_revision_ordered_review_requests_for_documents_and_replaced(
|
||||
review_request_queryset, names
|
||||
):
|
||||
"""Extracts all review requests for document names (including replaced ancestors), return them neatly sorted."""
|
||||
|
||||
names = set(names)
|
||||
|
@ -654,7 +665,13 @@ def extract_revision_ordered_review_requests_for_documents_and_replaced(review_r
|
|||
replaces = extract_complete_replaces_ancestor_mapping_for_docs(names)
|
||||
|
||||
requests_for_each_doc = defaultdict(list)
|
||||
for r in review_request_queryset.filter(doc__name__in=set(e for l in replaces.values() for e in l) | names).order_by("-time", "-id").iterator():
|
||||
for r in (
|
||||
review_request_queryset.filter(
|
||||
doc__name__in=set(e for l in replaces.values() for e in l) | names
|
||||
)
|
||||
.order_by("-time", "-id")
|
||||
.iterator(chunk_size=2000) # chunk_size not tested, using pre-Django 5 default value
|
||||
):
|
||||
requests_for_each_doc[r.doc.name].append(r)
|
||||
|
||||
# now collect in breadth-first order to keep the revision order intact
|
||||
|
|
|
@ -158,7 +158,7 @@ class SessionRequestTestCase(TestCase):
|
|||
list(TimerangeName.objects.filter(name__in=['thursday-afternoon-early', 'thursday-afternoon-late']).values('name'))
|
||||
)
|
||||
self.assertFalse(sessions[0].joint_with_groups.count())
|
||||
self.assertEqual(list(sessions[1].joint_with_groups.all()), [group3, group4])
|
||||
self.assertEqual(set(sessions[1].joint_with_groups.all()), {group3, group4})
|
||||
|
||||
# Check whether the updated data is visible on the view page
|
||||
r = self.client.get(redirect_url)
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
|
||||
import datetime
|
||||
|
||||
from functools import partialmethod
|
||||
|
||||
from django.contrib import messages
|
||||
from django.forms.formsets import formset_factory
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.utils.functional import curry
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
|
@ -215,7 +216,7 @@ def doc_detail(request, date, name):
|
|||
initial_state = {'state':doc.get_state(state_type).pk,
|
||||
'substate':tag}
|
||||
|
||||
# need to use curry here to pass custom variable to form init
|
||||
# need to use partialmethod here to pass custom variable to form init
|
||||
if doc.active_ballot():
|
||||
ballot_type = doc.active_ballot().ballot_type
|
||||
elif doc.type.slug == 'draft':
|
||||
|
@ -223,7 +224,7 @@ def doc_detail(request, date, name):
|
|||
else:
|
||||
ballot_type = BallotType.objects.get(doc_type=doc.type)
|
||||
BallotFormset = formset_factory(BallotForm, extra=0)
|
||||
BallotFormset.form.__init__ = curry(BallotForm.__init__, ballot_type=ballot_type)
|
||||
BallotFormset.form.__init__ = partialmethod(BallotForm.__init__, ballot_type=ballot_type)
|
||||
|
||||
agenda = agenda_data(date=date)
|
||||
header = get_section_header(doc, agenda)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Announcement{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
{% block title %}Areas{% endblock %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
{% block title %}Areas - People{% endblock %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
{% block title %}Areas - View{% endblock %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base_secr.html" %}
|
||||
{% load i18n %}
|
||||
{% load ietf_filters %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ title }}{% if user|has_role:"Secretariat" %} Secretariat Dashboard {% else %} IETF Dashboard {% endif %}{% endblock %}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base_secr_bootstrap.html" %}
|
||||
{% load i18n %}
|
||||
{% load ietf_filters %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{{ title }}{% if user|has_role:"Secretariat" %} Secretariat Dashboard {% else %} WG Chair Dashboard {% endif %}{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Confirm Cancel{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Confirm Delete{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Meetings - Add{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site_bootstrap.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Meetings{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Meetings - Blue Sheet{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Meetings - Edit{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Meetings{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Meetings{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles tz %}
|
||||
{% load static tz %}
|
||||
|
||||
{% block title %}Meetings{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Meetings{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Rolodex - Add{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Rolodex - Edit{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Rolodex - Search{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Sessions - Confirm{% endblock %}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
{% block title %}Sessions - Edit{% endblock %}
|
||||
|
||||
{% block extrahead %}{{ block.super }}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Sessions{% endblock %}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base_site.html" %}
|
||||
{% load ietf_filters %}
|
||||
{% load staticfiles %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Sessions{% endblock %}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue