Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ x-app-env: &x-app-env
TZ: Europe/Amsterdam
# WARNING: Strictly for development!
DISABLE_2FA: ${DISABLE_2FA:-True}
DEBUG: 'True'
DEBUG: 'False'
IS_HTTPS: 'no'
ALLOW_CUSTOM_JS: 'False'
# Overridable uwsgi settings
Expand Down
162 changes: 1 addition & 161 deletions src/eherkenning/static/eherkenning/eherkenning.styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ html {
rgba(255, 255, 255, 0.5) 5px,
rgba(255, 255, 255, 0.5) 10px
),
url('../images/eherkenning/backdrop.jpg');
url('backdrop.jpg');
background-size: auto, cover;
background-repeat: repeat, no-repeat;
background-position: center;
Expand Down Expand Up @@ -471,166 +471,6 @@ a.info:active span {
background-color: #fef1bc;
}

.uservoice {
font-weight: bold;
background: #f3f3f3;
text-align: right;
-moz-transform: skewX(-25deg);
-ms-transform: skewX(-25deg);
-webkit-transform: skewX(-25deg);
transform: skewX(-25deg);
min-height: 55px;
margin: 0.67em;
border: 2px solid #1c2f70;
padding: 10px;
line-height: 1.2em;
}
@media (min-width: 325px) {
.uservoice {
max-width: 53%;
min-width: 325px;
margin: 20px auto 40px auto;
}
}
@media (min-width: 1000px) {
.uservoice {
background: #1c2f70;
color: #fff;
position: fixed;
right: -26px;
top: 295px;
width: 190px;
min-width: auto;
max-width: none;
text-align: left;
}
}
@media (min-width: 1440px) {
.uservoice {
width: 315px;
top: 320px;
text-align: right;
}
}
.uservoice .inner {
-moz-transform: skewX(25deg);
-ms-transform: skewX(25deg);
-webkit-transform: skewX(25deg);
transform: skewX(25deg);
padding-right: 20%;
}
@media (min-width: 1000px) {
.uservoice .inner {
padding-left: 61px;
padding-right: 5px;
}
}
@media (min-width: 1440px) {
.uservoice .inner {
padding-left: 61px;
padding-right: 80px;
}
}
.uservoice .inner:before {
content: '';
width: 53px;
height: 40px;
background: url('../images/eherkenning/duimpje.svg') no-repeat;
display: block;
position: absolute;
left: -3px;
top: -4px;
}
.uservoice .inner:after {
content: '';
width: 30px;
height: 30px;
background: url('../images/eherkenning/pijltje-donker.svg') no-repeat;
display: block;
position: absolute;
right: 5px;
top: 15%;
}
@media (min-width: 1000px) {
.uservoice .inner:after {
background: none;
}
}
@media (min-width: 1440px) {
.uservoice .inner:after {
background: url('../images/eherkenning/pijltje.svg') no-repeat;
right: 30px;
}
}

.uservoice a {
text-decoration: none;
display: block;
width: 100%;
}
@media (min-width: 1000px) {
.uservoice a {
color: #fff;
display: inline;
}
.uservoice a:after {
content: '';
display: inline-block;
width: 18px;
height: 18px;
background: url('../images/eherkenning/pijltje.svg') no-repeat;
background-size: contain;
position: absolute;
margin-left: 5px;
}
}
@media (min-width: 1440px) {
.uservoice a:after {
display: none;
}
}
.uservoice a span.info {
position: relative;
z-index: 24;
}
.uservoice a:hover,
.uservoice a:focus,
.uservoice a:active {
z-index: 25;
}
.uservoice a span.info {
position: absolute;
left: -9000px;
width: 0;
overflow: hidden;
}
.uservoice a:hover span.info,
.uservoice a:focus span.info,
.uservoice a:active span.info {
display: block;
position: absolute;
top: 3em;
left: 1em;
width: 12em;
text-align: center;
background-color: #fef1bc;
color: #1c2f70;
}

.uservoice .second-line {
display: block;
}
@media (min-width: 1000px) {
.uservoice .second-line {
display: none;
}
}
@media (min-width: 1440px) {
.uservoice .second-line {
display: block;
}
}

#section-content .message .inner {
padding: 10px;
}
6 changes: 5 additions & 1 deletion src/open_inwoner/conf/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@

# The file storage engine to use when collecting static files with the
# collectstatic management command.
# STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
# NOTE: `STORAGES` (set in base.py) takes precedence over `STATICFILES_STORAGE`
# whenever both are defined, so the override has to happen on the dict itself.
STORAGES["staticfiles"]["BACKEND"] = (
"open_inwoner.utils.files.TolerantManifestStaticFilesStorage"
)

# Production logging facility.
handlers = ["console"] if LOG_STDOUT else ["django"]
Expand Down
32 changes: 32 additions & 0 deletions src/open_inwoner/utils/files.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.contrib.staticfiles.storage import ManifestStaticFilesStorage
from django.core.files.storage import FileSystemStorage


Expand All @@ -7,3 +8,34 @@ class OverwriteStorage(FileSystemStorage):
def get_available_name(self, name, max_length=None):
self.delete(name)
return name


# Static asset references that are known to be broken in a vendored/third-party
# package, and that we deliberately let ManifestStaticFilesStorage leave
# unhashed instead of hard-failing collectstatic. Keep this list narrow: any
# other missing reference should still fail the build loudly, exactly as
# ManifestStaticFilesStorage intends.
KNOWN_MISSING_STATIC_REFERENCES = {
# maykin-django-prosemirror==0.9.0 ships a minified bundle with a dangling
# `//# sourceMappingURL=bundle.js.map` comment, but the .map file itself
# isn't included in the wheel. Remove this entry once that's fixed
# upstream: https://github.com/maykinmedia/django-prosemirror
"js/bundle.js.map",
}


class TolerantManifestStaticFilesStorage(ManifestStaticFilesStorage):
"""
ManifestStaticFilesStorage that doesn't fail collectstatic over specific,
known-broken references to missing static files (see
``KNOWN_MISSING_STATIC_REFERENCES``). Every other missing reference still
raises, same as the base class.
"""

def hashed_name(self, name, content=None, filename=None):
try:
return super().hashed_name(name, content, filename)
except ValueError:
if self.clean_name(name) in KNOWN_MISSING_STATIC_REFERENCES:
return name
raise
67 changes: 45 additions & 22 deletions vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@ export default defineConfig(({ mode }) => {
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const isProduction = mode === 'production';

// Named entries, referenced by Django templates via their exact, unhashed
// `{% static %}` path - unlike code-split chunks, these must not carry a
// Vite content hash in their output filename.
const buildInput = {
// Frontend folder (new)
[`${paths.package.name}-frontend`]: path.resolve(
__dirname,
paths.frontendEntry
),
// Legacy CSS
[`${paths.package.name}-css`]: path.resolve(__dirname, paths.scssEntry),
// Legacy JS
[`${paths.package.name}-js`]: path.resolve(__dirname, paths.jsEntry),
// Admin overrides css
admin_overrides: path.resolve(__dirname, paths.adminOverridesEntry),
// PDF-P CSS
'pdf-p': path.resolve(__dirname, paths.pdfPortraitEntry),
// Django Admin JS.
'django-admin': path.resolve(__dirname, paths.djangoAdminEntry),
};
const buildInputNames = new Set(Object.keys(buildInput));

return {
plugins: [
preact({
Expand Down Expand Up @@ -68,32 +90,33 @@ export default defineConfig(({ mode }) => {

rollupOptions: {
// dest/source manager.
input: {
// Frontend folder (new)
[`${paths.package.name}-frontend`]: path.resolve(
__dirname,
paths.frontendEntry
),
// Legacy CSS
[`${paths.package.name}-css`]: path.resolve(
__dirname,
paths.scssEntry
),
// Legacy JS
[`${paths.package.name}-js`]: path.resolve(__dirname, paths.jsEntry),
// Admin overrides css
admin_overrides: path.resolve(__dirname, paths.adminOverridesEntry),
// PDF-P CSS
'pdf-p': path.resolve(__dirname, paths.pdfPortraitEntry),
// Django Admin JS.
'django-admin': path.resolve(__dirname, paths.djangoAdminEntry),
},
input: buildInput,

// Bundle file name manager.
output: {
entryFileNames: '[name].js',
chunkFileNames: '[name].bundle.js',
assetFileNames: '[name].[ext]',
// Chunks (dynamic imports) aren't referenced via Django's
// `{% static %}` tag or a rewritable url()/@import - they're
// loaded through Vite's own runtime, which bakes the filename in
// as a plain string at build time. Django's ManifestStaticFilesStorage
// can't see or rewrite that, so these need their own content hash
// from Vite directly.
chunkFileNames: '[name].[hash].bundle.js',
// Rollup emits CSS for both named entries (e.g. `open_inwoner-css`,
// referenced by Django templates via its exact, unhashed name) and
// for code-split chunks (e.g. a lazy-loaded component's CSS, only
// ever referenced from Vite's own runtime) through this same
// option. Only hash the latter - hashing an entry's output would
// break the hardcoded `{% static %}` reference to it.
assetFileNames: (assetInfo) => {
const baseName = (assetInfo.names?.[0] ?? '').replace(
/\.[^.]+$/,
''
);
return buildInputNames.has(baseName)
? '[name].[ext]'
: '[name].[hash].[ext]';
},
},
},
},
Expand Down
Loading