Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e634a960b | ||
|
|
19ec4b9520 | ||
|
|
fa81b64558 | ||
|
|
a2a9a773f4 | ||
|
|
72dba8bcfa | ||
|
|
256a81a770 | ||
|
|
56d143b74b | ||
|
|
fa61d9f5ae |
@@ -0,0 +1,14 @@
|
|||||||
|
# Houd de build-context klein (context is de repo-root)
|
||||||
|
.git
|
||||||
|
.vs
|
||||||
|
.env
|
||||||
|
app
|
||||||
|
Latex
|
||||||
|
Build
|
||||||
|
Proostenkrant.NET
|
||||||
|
docker
|
||||||
|
node_modules
|
||||||
|
**/bin
|
||||||
|
**/obj
|
||||||
|
*.pdf
|
||||||
|
*.log
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# ProostenKrant Environment Configuration
|
||||||
|
|
||||||
|
# Database Configuration
|
||||||
|
DB_HOST=mysql
|
||||||
|
DB_NAME=proostenkrant
|
||||||
|
DB_USER=proostenkrant
|
||||||
|
DB_PASSWORD=proostenkrant
|
||||||
|
|
||||||
|
# Application Configuration
|
||||||
|
UNIVERSAL_PASSWORD=ProostenKrant2024!
|
||||||
|
|
||||||
|
# PHP Configuration
|
||||||
|
UPLOAD_MAX_FILESIZE=50M
|
||||||
|
POST_MAX_SIZE=52M
|
||||||
|
MEMORY_LIMIT=512M
|
||||||
|
MAX_EXECUTION_TIME=300
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# Dependencies
|
||||||
|
/vendor
|
||||||
|
composer.lock
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
.docker
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# Artikeluploads via de website (runtime gegenereerd)
|
||||||
|
resources/images/articles/*
|
||||||
|
!resources/images/articles/.gitkeep
|
||||||
|
|
||||||
|
# Database dumps
|
||||||
|
*.sql
|
||||||
|
!deploy/mysql/init.sql
|
||||||
|
backup/
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
cache/
|
||||||
|
|
||||||
|
# Build files
|
||||||
|
/build
|
||||||
|
/obj
|
||||||
|
|
||||||
|
# Old project files
|
||||||
|
*.exe
|
||||||
|
*.pdb
|
||||||
|
*.dll
|
||||||
|
*.config
|
||||||
|
*.sln
|
||||||
|
|
||||||
|
# Blazor (.NET) build artifacts
|
||||||
|
src/**/bin/
|
||||||
|
src/**/obj/
|
||||||
|
*.user
|
||||||
@@ -1,495 +0,0 @@
|
|||||||
This is pdfTeX, Version 3.141592653-2.6-1.40.24 (TeX Live 2022) (preloaded format=pdflatex 2022.12.23) 23 DEC 2022 20:23
|
|
||||||
entering extended mode
|
|
||||||
restricted \write18 enabled.
|
|
||||||
%&-line parsing enabled.
|
|
||||||
**../Latex/Main.tex
|
|
||||||
(../Latex/Main.tex
|
|
||||||
LaTeX2e <2022-11-01> patch level 1
|
|
||||||
L3 programming layer <2022-12-17>
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/base/article.cls
|
|
||||||
Document Class: article 2022/07/02 v1.4n Standard LaTeX document class
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/base/size10.clo
|
|
||||||
File: size10.clo 2022/07/02 v1.4n Standard LaTeX file (size option)
|
|
||||||
)
|
|
||||||
\c@part=\count185
|
|
||||||
\c@section=\count186
|
|
||||||
\c@subsection=\count187
|
|
||||||
\c@subsubsection=\count188
|
|
||||||
\c@paragraph=\count189
|
|
||||||
\c@subparagraph=\count190
|
|
||||||
\c@figure=\count191
|
|
||||||
\c@table=\count192
|
|
||||||
\abovecaptionskip=\skip48
|
|
||||||
\belowcaptionskip=\skip49
|
|
||||||
\bibindent=\dimen140
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/base/inputenc.sty
|
|
||||||
Package: inputenc 2021/02/14 v1.3d Input encoding file
|
|
||||||
\inpenc@prehook=\toks16
|
|
||||||
\inpenc@posthook=\toks17
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/base/fontenc.sty
|
|
||||||
Package: fontenc 2021/04/29 v2.0v Standard LaTeX package
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/microtype/microtype.sty
|
|
||||||
Package: microtype 2022/06/23 v3.0f Micro-typographical refinements (RS)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/graphics/keyval.sty
|
|
||||||
Package: keyval 2022/05/29 v1.15 key=value parser (DPC)
|
|
||||||
\KV@toks@=\toks18
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/etoolbox/etoolbox.sty
|
|
||||||
Package: etoolbox 2020/10/05 v2.5k e-TeX tools for LaTeX (JAW)
|
|
||||||
\etb@tempcnta=\count193
|
|
||||||
)
|
|
||||||
\MT@toks=\toks19
|
|
||||||
\MT@tempbox=\box51
|
|
||||||
\MT@count=\count194
|
|
||||||
LaTeX Info: Redefining \noprotrusionifhmode on input line 1045.
|
|
||||||
LaTeX Info: Redefining \leftprotrusion on input line 1046.
|
|
||||||
LaTeX Info: Redefining \rightprotrusion on input line 1056.
|
|
||||||
LaTeX Info: Redefining \textls on input line 1234.
|
|
||||||
\MT@outer@kern=\dimen141
|
|
||||||
LaTeX Info: Redefining \textmicrotypecontext on input line 1858.
|
|
||||||
\MT@listname@count=\count195
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/microtype/microtype-pdftex.def
|
|
||||||
File: microtype-pdftex.def 2022/06/23 v3.0f Definitions specific to pdftex (RS)
|
|
||||||
|
|
||||||
LaTeX Info: Redefining \lsstyle on input line 900.
|
|
||||||
LaTeX Info: Redefining \lslig on input line 900.
|
|
||||||
\MT@outer@space=\skip50
|
|
||||||
)
|
|
||||||
Package microtype Info: Loading configuration file microtype.cfg.
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/microtype/microtype.cfg
|
|
||||||
File: microtype.cfg 2022/06/23 v3.0f microtype main configuration file (RS)
|
|
||||||
))
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/newspaper/newspaper.sty
|
|
||||||
Package: newspaper
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/yfonts/yfonts.sty
|
|
||||||
Package: yfonts 2019/04/04 v1.4 (WaS&NP)
|
|
||||||
Now handling font encoding LY ...
|
|
||||||
... no UTF-8 mapping file for font encoding LY
|
|
||||||
Now handling font encoding LYG ...
|
|
||||||
... no UTF-8 mapping file for font encoding LYG
|
|
||||||
LaTeX Info: Composite with undeclared \" in encoding LYG on input line 58.
|
|
||||||
)
|
|
||||||
\c@volume=\count196
|
|
||||||
\c@issue=\count197
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/babel/babel.sty
|
|
||||||
Package: babel 2022/11/30 3.83 The Babel package
|
|
||||||
\babel@savecnt=\count198
|
|
||||||
\U@D=\dimen142
|
|
||||||
\l@unhyphenated=\language87
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/babel/txtbabel.def)
|
|
||||||
\bbl@readstream=\read2
|
|
||||||
\bbl@dirlevel=\count199
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/babel-dutch/dutch.ldf
|
|
||||||
Language: dutch 2021/08/29 v3.8l Dutch support from the babel system
|
|
||||||
Package babel Info: Making " an active character on input line 127.
|
|
||||||
))
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/babel/locale/nl/babel-dutch.tex
|
|
||||||
Package babel Info: Importing font and identification data for dutch
|
|
||||||
(babel) from babel-nl.ini. Reported on input line 11.
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/graphics/graphicx.sty
|
|
||||||
Package: graphicx 2021/09/16 v1.2d Enhanced LaTeX Graphics (DPC,SPQR)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/graphics/graphics.sty
|
|
||||||
Package: graphics 2022/03/10 v1.4e Standard LaTeX Graphics (DPC,SPQR)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/graphics/trig.sty
|
|
||||||
Package: trig 2021/08/11 v1.11 sin cos tan (DPC)
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/graphics-cfg/graphics.cfg
|
|
||||||
File: graphics.cfg 2016/06/04 v1.11 sample graphics configuration
|
|
||||||
)
|
|
||||||
Package graphics Info: Driver file: pdftex.def on input line 107.
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/graphics-def/pdftex.def
|
|
||||||
File: pdftex.def 2022/09/22 v1.2b Graphics/color driver for pdftex
|
|
||||||
))
|
|
||||||
\Gin@req@height=\dimen143
|
|
||||||
\Gin@req@width=\dimen144
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/pgf/frontendlayer/tikz.sty
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/pgf/basiclayer/pgf.sty
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/pgf/utilities/pgfrcs.sty
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/utilities/pgfutil-common.tex
|
|
||||||
\pgfutil@everybye=\toks20
|
|
||||||
\pgfutil@tempdima=\dimen145
|
|
||||||
\pgfutil@tempdimb=\dimen146
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/utilities/pgfutil-common-lists.tex)
|
|
||||||
) (c:/texlive/2022/texmf-dist/tex/generic/pgf/utilities/pgfutil-latex.def
|
|
||||||
\pgfutil@abb=\box52
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/utilities/pgfrcs.code.tex
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/pgf.revision.tex)
|
|
||||||
Package: pgfrcs 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
))
|
|
||||||
Package: pgf 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/pgf/basiclayer/pgfcore.sty
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/pgf/systemlayer/pgfsys.sty
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/systemlayer/pgfsys.code.tex
|
|
||||||
Package: pgfsys 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex
|
|
||||||
\pgfkeys@pathtoks=\toks21
|
|
||||||
\pgfkeys@temptoks=\toks22
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/utilities/pgfkeysfiltered.code.tex
|
|
||||||
\pgfkeys@tmptoks=\toks23
|
|
||||||
))
|
|
||||||
\pgf@x=\dimen147
|
|
||||||
\pgf@y=\dimen148
|
|
||||||
\pgf@xa=\dimen149
|
|
||||||
\pgf@ya=\dimen150
|
|
||||||
\pgf@xb=\dimen151
|
|
||||||
\pgf@yb=\dimen152
|
|
||||||
\pgf@xc=\dimen153
|
|
||||||
\pgf@yc=\dimen154
|
|
||||||
\pgf@xd=\dimen155
|
|
||||||
\pgf@yd=\dimen156
|
|
||||||
\w@pgf@writea=\write3
|
|
||||||
\r@pgf@reada=\read3
|
|
||||||
\c@pgf@counta=\count266
|
|
||||||
\c@pgf@countb=\count267
|
|
||||||
\c@pgf@countc=\count268
|
|
||||||
\c@pgf@countd=\count269
|
|
||||||
\t@pgf@toka=\toks24
|
|
||||||
\t@pgf@tokb=\toks25
|
|
||||||
\t@pgf@tokc=\toks26
|
|
||||||
\pgf@sys@id@count=\count270
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/systemlayer/pgf.cfg
|
|
||||||
File: pgf.cfg 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
)
|
|
||||||
Driver file for pgf: pgfsys-pdftex.def
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-pdftex.def
|
|
||||||
File: pgfsys-pdftex.def 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/systemlayer/pgfsys-common-pdf.def
|
|
||||||
File: pgfsys-common-pdf.def 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
)))
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/systemlayer/pgfsyssoftpath.code.tex
|
|
||||||
File: pgfsyssoftpath.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgfsyssoftpath@smallbuffer@items=\count271
|
|
||||||
\pgfsyssoftpath@bigbuffer@items=\count272
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/systemlayer/pgfsysprotocol.code.tex
|
|
||||||
File: pgfsysprotocol.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
)) (c:/texlive/2022/texmf-dist/tex/latex/xcolor/xcolor.sty
|
|
||||||
Package: xcolor 2022/06/12 v2.14 LaTeX color extensions (UK)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/graphics-cfg/color.cfg
|
|
||||||
File: color.cfg 2016/01/02 v1.6 sample color configuration
|
|
||||||
)
|
|
||||||
Package xcolor Info: Driver file: pdftex.def on input line 227.
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/graphics/mathcolor.ltx)
|
|
||||||
Package xcolor Info: Model `cmy' substituted by `cmy0' on input line 1353.
|
|
||||||
Package xcolor Info: Model `hsb' substituted by `rgb' on input line 1357.
|
|
||||||
Package xcolor Info: Model `RGB' extended on input line 1369.
|
|
||||||
Package xcolor Info: Model `HTML' substituted by `rgb' on input line 1371.
|
|
||||||
Package xcolor Info: Model `Hsb' substituted by `hsb' on input line 1372.
|
|
||||||
Package xcolor Info: Model `tHsb' substituted by `hsb' on input line 1373.
|
|
||||||
Package xcolor Info: Model `HSB' substituted by `hsb' on input line 1374.
|
|
||||||
Package xcolor Info: Model `Gray' substituted by `gray' on input line 1375.
|
|
||||||
Package xcolor Info: Model `wave' substituted by `hsb' on input line 1376.
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcore.code.tex
|
|
||||||
Package: pgfcore 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathcalc.code.tex
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathutil.code.tex)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathparser.code.tex
|
|
||||||
\pgfmath@dimen=\dimen157
|
|
||||||
\pgfmath@count=\count273
|
|
||||||
\pgfmath@box=\box53
|
|
||||||
\pgfmath@toks=\toks27
|
|
||||||
\pgfmath@stack@operand=\toks28
|
|
||||||
\pgfmath@stack@operation=\toks29
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.code.tex
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.basic.code.te
|
|
||||||
x)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.trigonometric
|
|
||||||
.code.tex)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.random.code.t
|
|
||||||
ex)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.comparison.co
|
|
||||||
de.tex)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.base.code.tex
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.round.code.te
|
|
||||||
x)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.misc.code.tex
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathfunctions.integerarithm
|
|
||||||
etics.code.tex)))
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmathfloat.code.tex
|
|
||||||
\c@pgfmathroundto@lastzeros=\count274
|
|
||||||
))
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfint.code.tex)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepoints.code.tex
|
|
||||||
File: pgfcorepoints.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgf@picminx=\dimen158
|
|
||||||
\pgf@picmaxx=\dimen159
|
|
||||||
\pgf@picminy=\dimen160
|
|
||||||
\pgf@picmaxy=\dimen161
|
|
||||||
\pgf@pathminx=\dimen162
|
|
||||||
\pgf@pathmaxx=\dimen163
|
|
||||||
\pgf@pathminy=\dimen164
|
|
||||||
\pgf@pathmaxy=\dimen165
|
|
||||||
\pgf@xx=\dimen166
|
|
||||||
\pgf@xy=\dimen167
|
|
||||||
\pgf@yx=\dimen168
|
|
||||||
\pgf@yy=\dimen169
|
|
||||||
\pgf@zx=\dimen170
|
|
||||||
\pgf@zy=\dimen171
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathconstruct.cod
|
|
||||||
e.tex
|
|
||||||
File: pgfcorepathconstruct.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgf@path@lastx=\dimen172
|
|
||||||
\pgf@path@lasty=\dimen173
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathusage.code.te
|
|
||||||
x
|
|
||||||
File: pgfcorepathusage.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgf@shorten@end@additional=\dimen174
|
|
||||||
\pgf@shorten@start@additional=\dimen175
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcorescopes.code.tex
|
|
||||||
File: pgfcorescopes.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgfpic=\box54
|
|
||||||
\pgf@hbox=\box55
|
|
||||||
\pgf@layerbox@main=\box56
|
|
||||||
\pgf@picture@serial@count=\count275
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcoregraphicstate.code
|
|
||||||
.tex
|
|
||||||
File: pgfcoregraphicstate.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgflinewidth=\dimen176
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransformations.c
|
|
||||||
ode.tex
|
|
||||||
File: pgfcoretransformations.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgf@pt@x=\dimen177
|
|
||||||
\pgf@pt@y=\dimen178
|
|
||||||
\pgf@pt@temp=\dimen179
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcorequick.code.tex
|
|
||||||
File: pgfcorequick.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreobjects.code.tex
|
|
||||||
File: pgfcoreobjects.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepathprocessing.co
|
|
||||||
de.tex
|
|
||||||
File: pgfcorepathprocessing.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcorearrows.code.tex
|
|
||||||
File: pgfcorearrows.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgfarrowsep=\dimen180
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreshade.code.tex
|
|
||||||
File: pgfcoreshade.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgf@max=\dimen181
|
|
||||||
\pgf@sys@shading@range@num=\count276
|
|
||||||
\pgf@shadingcount=\count277
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreimage.code.tex
|
|
||||||
File: pgfcoreimage.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcoreexternal.code.tex
|
|
||||||
File: pgfcoreexternal.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgfexternal@startupbox=\box57
|
|
||||||
))
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcorelayers.code.tex
|
|
||||||
File: pgfcorelayers.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcoretransparency.code
|
|
||||||
.tex
|
|
||||||
File: pgfcoretransparency.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcorepatterns.code.tex
|
|
||||||
File: pgfcorepatterns.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
) (c:/texlive/2022/texmf-dist/tex/generic/pgf/basiclayer/pgfcorerdf.code.tex
|
|
||||||
File: pgfcorerdf.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
))) (c:/texlive/2022/texmf-dist/tex/generic/pgf/modules/pgfmoduleshapes.code.te
|
|
||||||
x
|
|
||||||
File: pgfmoduleshapes.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgfnodeparttextbox=\box58
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/modules/pgfmoduleplot.code.tex
|
|
||||||
File: pgfmoduleplot.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-0-65.st
|
|
||||||
y
|
|
||||||
Package: pgfcomp-version-0-65 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgf@nodesepstart=\dimen182
|
|
||||||
\pgf@nodesepend=\dimen183
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/pgf/compatibility/pgfcomp-version-1-18.st
|
|
||||||
y
|
|
||||||
Package: pgfcomp-version-1-18 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
)) (c:/texlive/2022/texmf-dist/tex/latex/pgf/utilities/pgffor.sty
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/pgf/utilities/pgfkeys.sty
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/utilities/pgfkeys.code.tex))
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/pgf/math/pgfmath.sty
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex))
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/utilities/pgffor.code.tex
|
|
||||||
Package: pgffor 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/math/pgfmath.code.tex)
|
|
||||||
\pgffor@iter=\dimen184
|
|
||||||
\pgffor@skip=\dimen185
|
|
||||||
\pgffor@stack=\toks30
|
|
||||||
\pgffor@toks=\toks31
|
|
||||||
))
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex
|
|
||||||
Package: tikz 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/libraries/pgflibraryplothandlers.co
|
|
||||||
de.tex
|
|
||||||
File: pgflibraryplothandlers.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgf@plot@mark@count=\count278
|
|
||||||
\pgfplotmarksize=\dimen186
|
|
||||||
)
|
|
||||||
\tikz@lastx=\dimen187
|
|
||||||
\tikz@lasty=\dimen188
|
|
||||||
\tikz@lastxsaved=\dimen189
|
|
||||||
\tikz@lastysaved=\dimen190
|
|
||||||
\tikz@lastmovetox=\dimen191
|
|
||||||
\tikz@lastmovetoy=\dimen192
|
|
||||||
\tikzleveldistance=\dimen193
|
|
||||||
\tikzsiblingdistance=\dimen194
|
|
||||||
\tikz@figbox=\box59
|
|
||||||
\tikz@figbox@bg=\box60
|
|
||||||
\tikz@tempbox=\box61
|
|
||||||
\tikz@tempbox@bg=\box62
|
|
||||||
\tikztreelevel=\count279
|
|
||||||
\tikznumberofchildren=\count280
|
|
||||||
\tikznumberofcurrentchild=\count281
|
|
||||||
\tikz@fig@count=\count282
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/modules/pgfmodulematrix.code.tex
|
|
||||||
File: pgfmodulematrix.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
\pgfmatrixcurrentrow=\count283
|
|
||||||
\pgfmatrixcurrentcolumn=\count284
|
|
||||||
\pgf@matrix@numberofcolumns=\count285
|
|
||||||
)
|
|
||||||
\tikz@expandcount=\count286
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/generic/pgf/frontendlayer/tikz/libraries/tikzli
|
|
||||||
brarytopaths.code.tex
|
|
||||||
File: tikzlibrarytopaths.code.tex 2021/05/15 v3.1.9a (3.1.9a)
|
|
||||||
)))
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/tools/multicol.sty
|
|
||||||
Package: multicol 2021/11/30 v1.9d multicolumn formatting (FMi)
|
|
||||||
\c@tracingmulticols=\count287
|
|
||||||
\mult@box=\box63
|
|
||||||
\multicol@leftmargin=\dimen195
|
|
||||||
\c@unbalance=\count288
|
|
||||||
\c@collectmore=\count289
|
|
||||||
\doublecol@number=\count290
|
|
||||||
\multicoltolerance=\count291
|
|
||||||
\multicolpretolerance=\count292
|
|
||||||
\full@width=\dimen196
|
|
||||||
\page@free=\dimen197
|
|
||||||
\premulticols=\dimen198
|
|
||||||
\postmulticols=\dimen199
|
|
||||||
\multicolsep=\skip51
|
|
||||||
\multicolbaselineskip=\skip52
|
|
||||||
\partial@page=\box64
|
|
||||||
\last@line=\box65
|
|
||||||
\maxbalancingoverflow=\dimen256
|
|
||||||
\mult@rightbox=\box66
|
|
||||||
\mult@grightbox=\box67
|
|
||||||
\mult@firstbox=\box68
|
|
||||||
\mult@gfirstbox=\box69
|
|
||||||
\@tempa=\box70
|
|
||||||
\@tempa=\box71
|
|
||||||
\@tempa=\box72
|
|
||||||
\@tempa=\box73
|
|
||||||
\@tempa=\box74
|
|
||||||
\@tempa=\box75
|
|
||||||
\@tempa=\box76
|
|
||||||
\@tempa=\box77
|
|
||||||
\@tempa=\box78
|
|
||||||
\@tempa=\box79
|
|
||||||
\@tempa=\box80
|
|
||||||
\@tempa=\box81
|
|
||||||
\@tempa=\box82
|
|
||||||
\@tempa=\box83
|
|
||||||
\@tempa=\box84
|
|
||||||
\@tempa=\box85
|
|
||||||
\@tempa=\box86
|
|
||||||
\@tempa=\box87
|
|
||||||
\@tempa=\box88
|
|
||||||
\@tempa=\box89
|
|
||||||
\@tempa=\box90
|
|
||||||
\@tempa=\box91
|
|
||||||
\@tempa=\box92
|
|
||||||
\@tempa=\box93
|
|
||||||
\@tempa=\box94
|
|
||||||
\@tempa=\box95
|
|
||||||
\@tempa=\box96
|
|
||||||
\@tempa=\box97
|
|
||||||
\@tempa=\box98
|
|
||||||
\@tempa=\box99
|
|
||||||
\@tempa=\box100
|
|
||||||
\@tempa=\box101
|
|
||||||
\@tempa=\box102
|
|
||||||
\@tempa=\box103
|
|
||||||
\@tempa=\box104
|
|
||||||
\@tempa=\box105
|
|
||||||
\c@minrows=\count293
|
|
||||||
\c@columnbadness=\count294
|
|
||||||
\c@finalcolumnbadness=\count295
|
|
||||||
\last@try=\dimen257
|
|
||||||
\multicolovershoot=\dimen258
|
|
||||||
\multicolundershoot=\dimen259
|
|
||||||
\mult@nat@firstbox=\box106
|
|
||||||
\colbreak@box=\box107
|
|
||||||
\mc@col@check@num=\count296
|
|
||||||
)
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/subfiles/subfiles.sty
|
|
||||||
Package: subfiles 2020/11/14 v2.2 Multi-file projects (package)
|
|
||||||
|
|
||||||
(c:/texlive/2022/texmf-dist/tex/latex/import/import.sty
|
|
||||||
Package: import 2020/04/01 v 6.2
|
|
||||||
))
|
|
||||||
|
|
||||||
! LaTeX Error: File `newspaper-mod.sty' not found.
|
|
||||||
|
|
||||||
Type X to quit or <RETURN> to proceed,
|
|
||||||
or enter new name. (Default extension: sty)
|
|
||||||
|
|
||||||
Enter file name:
|
|
||||||
! Emergency stop.
|
|
||||||
<read *>
|
|
||||||
|
|
||||||
l.35 ^^M
|
|
||||||
|
|
||||||
End of file on the terminal!
|
|
||||||
|
|
||||||
|
|
||||||
Here is how much of TeX's memory you used:
|
|
||||||
14643 strings out of 476093
|
|
||||||
293783 string characters out of 5795582
|
|
||||||
1848787 words of memory out of 5000000
|
|
||||||
34817 multiletter control sequences out of 15000+600000
|
|
||||||
513050 words of font info for 33 fonts, out of 8000000 for 9000
|
|
||||||
1141 hyphenation exceptions out of 8191
|
|
||||||
84i,0n,89p,415b,329s stack positions out of 10000i,1000n,20000p,200000b,200000s
|
|
||||||
! ==> Fatal error occurred, no output PDF file produced!
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<configuration>
|
|
||||||
<startup>
|
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
|
||||||
</startup>
|
|
||||||
</configuration>
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
\headline{Naar spoed in plaats van naar de film.}
|
|
||||||
\byline{22-10-2022}{Dien}
|
|
||||||
\showOneWriter{Images/ProfilePictures/Dien.jpg}{Dien}
|
|
||||||
\par Zoals afgesproken op het feest van bomma ,zouden we met al diegenen die konden naar de film"Onze natuur" gaan op donderdag 20 oktober. Daar stak de natuur echter een stokje voor in mijn geval. Vanaf die donderdagmorgen voelde ik me niet goed en moest ik overgeven. Als ik dan wat water dronk kwam dit er direct terug uit gepaard met een ondraaglijke pijn. Een doktersbezoek bracht weinig soelaas en hij vermoede dat dit opnieuw een galcrisis was. Tien jaar geleden liet ik naar aanleiding van een galcrisis mijn galblaas verwijderen. Het was afwachten tot er weer aanval aankwam en dan moest ik onmiddellijk naar spoed. Diezelfde avond was het van dat. Weer heel hevige pijn en Michel reed met mij naar spoed. Onze naftbak was leeg en Michel wou eerst nog gaan tanken maar dat was geen optie. Het zag ondertussen zwart voor mijn ogen dus maken dat we bij de spoed waren. Daar aangekomen was de grootste pijn weer gaan liggen en hebben ze alles gechecked. Het bleek niet om galstenen maar gruis te gaan waar niet veel aan gedaan kan worden. We hebben 3 uren op spoed gezeten en werden dan naar huis gestuurd met de boodschap dat het afwachten was tot de volgende aanval zou komen en dan naar een specialist te gaan. Dat is dan ook de natuur zeker.
|
|
||||||
\showInlineImage{Images/Articles/Naar spoed in plaats van naar de film..jpg}{Naar spoed in plaats van naar de film.}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Kerstgrotten in Valkenburg}
|
|
||||||
\byline{15-12-2022}{Diede}
|
|
||||||
\showTwoWriters{Images/ProfilePictures/Diede.jpg}{Diede}{Images/ProfilePictures/Vincent.jpg}{Vincent}
|
|
||||||
\par Op woensdag 15 december gingen Vincent en ik naar de grotten in Valkenburg. Daar was een heuse 'kerststad' ingericht. Doorheen heel de stad hingen kerstlichtjes en in de grotten stonden grote lichtgevende standbeelden. Daarnaast waren er ook allemaal kraampjes waar ze verschillende dingen verkochten (waarvan het meeste jammer genoeg plastic rommel was). Om even een pauze te nemen van het slenteren tussen de oude mensjes, die trouwens met hele bussen aankwamen, hebben we een Glühwein gedronken in een barretje in de grot, hmm! 'S avonds zijn we weer teruggereden naar huis waarna we nog een kerstfilm gekeken hebben om de dag in thema af te sluiten.
|
|
||||||
\showInlineImage{Images/Articles/Kerstgrotten in Valkenburg.jpg}{Kerstgrotten in Valkenburg}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Reünie in Tübingen}
|
|
||||||
\byline{27-11-2022}{Diede}
|
|
||||||
\showOneWriter{Images/ProfilePictures/Diede.jpg}{Diede}
|
|
||||||
\par Ik ben een weekendje naar Tübingen geweest om af te spreken met enkele vrienden van Erasmus. Na een treinrit van 9 uur lang kwam ik aan op het kot van Lena die studeert in Tübingen. De volgende dag zijn ook Celine (uit Duitsland) en Roosa (uit Finland) aangekomen. Na een verkenning van de stad, zijn we allemaal samen naar de kerstmarkt in Esslingen geweest die een middeleeuws thema had. 'S avonds zijn we nog uitgeweest waarna we de volgende dag rustig aan hebben gedaan. Roosa en ik moesten de volgende ochtend heel vroeg weer weg aangezien onze busrit 12 uur ging duren. Uiteindelijk was het dat allemaal waard en was het superfijn om iedereen weer terug te zien!
|
|
||||||
\showInlineImage{Images/Articles/Reünie in Tübingen.jpg}{Reünie in Tübingen}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Mid weekje Oostende}
|
|
||||||
\byline{14-12-2022}{Michel}
|
|
||||||
\showOneWriter{Images/ProfilePictures/Michel.jpg}{Michel}
|
|
||||||
\par Afgelopen (mid)week trokken Dien en ik richting ‘t zeetje om eens even uit te waaien en de dagelijkse rush te ontlopen. Na het nodige slenterwerk langs de kustlijn, de streetart en de kerstrommel (in sommige kringen ook omschreven als ‘versiering ‘) gingen we wat cultuur, eten en drinken verorberen in het voormalig postgebouw van Oostende. Terwijl we genoten van de typische lokale gerechten (vegan Tajine en Pad Thai ;-) hoorde we dat in de foyer van het gebouw een Discjockey den boel serieus in de fik stak… ambiance en muziek uit de 80ies tot heden. Wat bleek, niemand minder dan Jackie Dewaele alias nonkel Bob alias Zaki stond er het beste van zichzelf te geven op de gezegende leeftijd van 77 , voor de jonge lezers: vader van de gebroeders DeWaele, 2manyDJ’s, Soulwax, studio Deewee. Efkes eten werd al snel een stomend dansfeest… Uustend is vree wijs wa!
|
|
||||||
\showInlineImage{Images/Articles/Mid weekje Oostende.jpg}{Mid weekje Oostende}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Er groeien vogels aan de takken. }
|
|
||||||
\byline{12-10-2022}{Chris}
|
|
||||||
\showOneWriter{Images/ProfilePictures/Chris.jpg}{Chris}
|
|
||||||
\par Er worden bomen geveld in de tuin. Het lijkt wel of zij dat weten. De vogels, die nog een laatste keer gaan zitten op de takken, die er straks niet meer zullen zijn. Zo schoon.
|
|
||||||
\showInlineImage{Images/Articles/Er groeien vogels aan de takken. .jpg}{Er groeien vogels aan de takken. }
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Feest bij de familie Ravers}
|
|
||||||
\byline{18-12-2022}{Dien}
|
|
||||||
\showOneWriter{Images/ProfilePictures/Dien.jpg}{Dien}
|
|
||||||
\par Op 18 december had moeke en vake een feest georganiseerd in het Veldekeshof in Turnhout. Niet speciaal voor kerstmis maar zomaar, zodat we mekaar allemaal nog eens zagen. Moeke had een restaurant gezocht waar iedereen zijn gading kon vinden in het aanbod deden. In het Veldekeshof in Turnhout,kon men aan haar wens voldoen. Het werd een gezellig samenzijn en veel bijkletsen. Zo bleek dat de vriend van Zoe (dochter van Kristel) in Schoonbroek in de chiro te zitten. Ik had voor de verandering nog eens voor videe gekozen. Dat was zo ongeveer 3 jaar geleden dat ik dat nog had gegeten. Na mijn eerste hap werd er plots gezegd dat de videe niet meer goed was. Ikzelf proefde niks speciaal maar ik vind dat kip sowieso stinkt. Na 3 jaar wist ik niet meer wat de normale smaak van kip is. De anderen bleven overtuigd dat de videe slecht was. De kok bleef er heel rustig onder hoewel hij zelf niets abnormaals proefde aan de videe. We kregen allemaal iets anders aangeboden,iedereen bleef heel rustig en het probleem was opgelost. Ik had nog getwijfeld om toch voor de vegan menu te gaan. Had ik dat maar gedaan want die thaise curry was overheerlijk volgens de vegans. Volgens mij was dit een teken, daarom heb ik besloten om vanaf januari ook veganist te worden.
|
|
||||||
\showInlineImage{Images/Articles/Feest bij de familie Ravers.jpg}{Feest bij de familie Ravers}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Kerstwandeling bijna fataal voor Saartje}
|
|
||||||
\byline{17-12-2022}{Daan}
|
|
||||||
\showTwoWriters{Images/ProfilePictures/Daan.jpg}{Daan}{Images/ProfilePictures/Kyala.jpg}{Kyala}
|
|
||||||
\par Op 17 december organiseerden de grootouders van Kyala een kerstwandeling voor de leden van hun dansclub. Kyala en ik beheerden één van de drie standjes die als tussenstop voor de wandelaars dienden. Het standje bevond zich op de oprit van een bevriend koppel van Kyala's grootouders. Toen de laatste wandelaars weer vertrokken waren en we één en ander binnen wilden zetten, sloot ik de deur achter me. Deze sloot echter niet volledig en ging langzaam terug open. Voordat we er erg in hadden, spurtte Saartje, het hondje dat in het huis woonde, naar de tuin. Wat volgde was een achtervolging op leven en dood; de poort stond namelijk wagenwijd open en kon enkel elektrisch worden gesloten door de eigenaars, die niet thuis waren. Na enkele tevergeefse pogingen om het hondje te vangen, was het zò bang van ons dat het vanzelf terug naar binnen vluchtte. Eind goed, al goed!
|
|
||||||
\showInlineImage{Images/Articles/Kerstwandeling bijna fataal voor Saartje.jpg}{Kerstwandeling bijna fataal voor Saartje}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Eenhoornfeestje in Kasterlee}
|
|
||||||
\byline{18-12-2022}{Mathilde}
|
|
||||||
\showTwoWriters{Images/ProfilePictures/Mathilde.jpg}{Mathilde}{Images/ProfilePictures/Pieter.jpg}{Pieter}
|
|
||||||
\par Zondag hebben we de vijfde verjaardag van Lise gevierd, het nichtje van Pieter. Het thema van het feestje was 'eenhoorns'. Er was eenhoorntaart en we mochten allemaal een hoorn opzetten. Wat zagen we er goed uit!
|
|
||||||
\showInlineImage{Images/Articles/Eenhoornfeestje in Kasterlee.jpg}{Eenhoornfeestje in Kasterlee}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Overlijdensbericht}
|
|
||||||
\byline{14-12-2022}{Gunter}
|
|
||||||
\showOneWriter{Images/ProfilePictures/Gunter.jpg}{Gunter}
|
|
||||||
\par Na 18 jaar melden wij dat Mario Proost is overleden. Hij woonde in de Kloosterstraat in Retie. Hij was de oudste kat van deze straat. Hij was een vondeling die door Daan en Dries destijds werd gevonden toen hij slechts enkele weken oud was, De laatste jaren sukkelde hij met zijn gezondheid maar bleef trouw op post. Muizen vangen lukte niet meer omdat hij 1 te traag was en 2 zijn tanden verloren had. Hij is de enige die erin slaagde om 18 jaar te overleven langs een drukke baan Hij stak de baan over als geen ander. RIP Mario dit melden zijn vinders Daan Dries, zijn verzorgers Gunter Inge, zijn psycho sociaal ondersteuningsteam Lucca en Kyala
|
|
||||||
\showInlineImage{Images/Articles/Overlijdensbericht.jpg}{Overlijdensbericht}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Marathon Kasterlee}
|
|
||||||
\byline{13-11-2022}{Gunter}
|
|
||||||
\showOneWriter{Images/ProfilePictures/Gunter.jpg}{Gunter}
|
|
||||||
\par Na een relatief beperkte training nam Inge deel aan de Marathon van Kasterlee. Dit was haar 65ste Marathon ondertussen wat op zich al een opsteker is. Gunter heeft 1 ronde van de 2 meegelopen, ja idd een halve marathon dus voor de slimme lezers:) Hij verdiende geen podiumplaats, Inge daarentegen wel, zij was de snelste vrouwelijke loopster van Kasterlee, thuis mocht ze ook op het podium niet omwille van de snelste, wel omwille van de beste:) en niet alleen van Kasterlee. Voor de geïnteresseerden haar tijd lag net binnen de 4 uren, opmerkelijk want daar mee was haar tijd sneller dan de vorige Marathon ondanks minder training. Leeftijd heeft op haar geen vat
|
|
||||||
\showInlineImage{Images/Articles/Marathon Kasterlee.jpg}{Marathon Kasterlee}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Lichtjeswandeling in Brussel}
|
|
||||||
\byline{9-12-2022}{Douwe}
|
|
||||||
\showTwoWriters{Images/ProfilePictures/Douwe.jpg}{Douwe}{Images/ProfilePictures/Pauline.jpg}{Pauline}
|
|
||||||
\par Op 9 december gingen Douwe en ik naar de lichtjeswandeling in Brussel. Dien en Michel moesten die avond ook in Brussel zijn voor een concert van Intergalactic Lovers, dus we besloten om met z'n vieren naar Brussel te rijden. De rit door het Centrum van Brussel was al een activiteit op zich, maar de rijkunsten van Michel hebben ons veilig tot aan de Ancienne Belgique gebracht. Na een lekkere tapas-maaltijd in een gezellige maar ietwat chaotische Spaanse tent, begonnen we aan onze wandeling. Brussel was prachtig versierd met allerlei lichtjes en er was ook een speciale wandeling voorzien met neon-verlichte geometrische figuren. Een gezellige (maar koude) avond dus, zeker voor herhaling vatbaar!
|
|
||||||
\showInlineImage{Images/Articles/Lichtjeswandeling in Brussel.jpg}{Lichtjeswandeling in Brussel}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Cedric de ochtendwandelaar}
|
|
||||||
\byline{11-12-2022}{Cedric}
|
|
||||||
\showTwoWriters{Images/ProfilePictures/Cedric.jpg}{Cedric}{Images/ProfilePictures/Annick.jpg}{Annick}
|
|
||||||
\par Elke zondag doet Cedric met zijn leefgroep een ochtendwandeling. Hij geniet hier zo van
|
|
||||||
\showInlineImage{Images/Articles/Cedric de ochtendwandelaar.jpg}{Cedric de ochtendwandelaar}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Op stap met de Singel}
|
|
||||||
\byline{11-12-2022}{Annick}
|
|
||||||
\showOneWriter{Images/ProfilePictures/Annick.jpg}{Annick}
|
|
||||||
\par Op zo 11/12/22 met de Singel naar’t Buitenveld in Retie. ‘t Was er héél druk maar wel gezellig .
|
|
||||||
\showInlineImage{Images/Articles/Op stap met de Singel.jpg}{Op stap met de Singel}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{ho.. ho.. ho...}
|
|
||||||
\byline{22-12-2022}{Annick}
|
|
||||||
\showOneWriter{Images/ProfilePictures/Annick.jpg}{Annick}
|
|
||||||
\par Heidi en ik waren gezellig aan het winkelen op de markt van Mol tot wij plots de enige en echte Kerstman tegenkwamen. Hij zei voornamelijk "Ho….ho….ho…..", maar ja... wij verstaan ook gewoon niets van dat Fins...
|
|
||||||
\showInlineImage{Images/Articles/ho.. ho.. ho....jpg}{ho.. ho.. ho...}
|
|
||||||
|
|
||||||
|
|
||||||
\headline{Middeleeuws eten en een IT job: the story of a man who did it all}
|
|
||||||
\byline{15-12-2022}{Dries}
|
|
||||||
\showTwoWriters{Images/ProfilePictures/Dries.jpg}{Dries}{Images/ProfilePictures/Lucca.jpg}{Lucca}
|
|
||||||
\par Dries 26 geworden, lekker gaan eten in herberg het wilde zwijn (zijn favoriet middeleeuws restaurant). Hij heeft van mij en de vrienden een hele dure en goede game chair gekregen waar hij heel freet op is en niemeer uit zijn kamertje komt. Dries heeft ook een nieuwe job in de IT (1ste job id IT), en hij ziet het volledig zitten! Wij plannen ondertussen een weekje Schotland voor tijdens ons verlof, haggis uitproberen en de highland games checken.
|
|
||||||
\showInlineImage{Images/Articles/Middeleeuws eten en een IT job the story of a man who did it all.jpg}{Middeleeuws eten en een IT job the story of a man who did it all}
|
|
||||||
|
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 461 KiB |
|
Before Width: | Height: | Size: 3.2 MiB |
|
Before Width: | Height: | Size: 263 KiB |
|
Before Width: | Height: | Size: 912 KiB |
|
Before Width: | Height: | Size: 4.2 MiB |
|
Before Width: | Height: | Size: 289 KiB |
|
Before Width: | Height: | Size: 200 KiB |
|
Before Width: | Height: | Size: 823 KiB |
|
Before Width: | Height: | Size: 195 KiB |
|
Before Width: | Height: | Size: 195 KiB |
|
Before Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 297 KiB |
|
Before Width: | Height: | Size: 4.4 MiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 335 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 955 KiB |
@@ -1,6 +0,0 @@
|
|||||||
\relax
|
|
||||||
\providecommand\babel@aux[2]{}
|
|
||||||
\@nameuse{bbl@beforestart}
|
|
||||||
\catcode `"\active
|
|
||||||
\babel@aux{dutch}{}
|
|
||||||
\gdef \@abspage@last{5}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
\usepackage{newtxtext,newtxmath}
|
|
||||||
\usepackage{etoolbox}
|
|
||||||
\newcommand{\headlinestyle}{\itshape\huge}
|
|
||||||
\newcommand{\bylinestyle}{\scshape\Large}
|
|
||||||
\patchcmd{\headline}{#1}{\headlinestyle #1}{}{}
|
|
||||||
\patchcmd{\byline}{#1}{\bylinestyle #1}{}{}
|
|
||||||
|
|
||||||
\newcommand{\showInlineImage}[2]
|
|
||||||
{
|
|
||||||
$
|
|
||||||
\begin{array}{c}
|
|
||||||
\resizebox{0.8\columnwidth}{!}{\includegraphics{#1}} \\
|
|
||||||
$#2$
|
|
||||||
\end{array}
|
|
||||||
$
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
\newcommand{\showOneWriter}[2]
|
|
||||||
{
|
|
||||||
$
|
|
||||||
\hfill
|
|
||||||
\begin{array}{c}
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\clip (0,0) circle (0.2\columnwidth) ;
|
|
||||||
\node[anchor=center] at (0,0) {\includegraphics[width=0.4\columnwidth]{#1}};
|
|
||||||
\end{tikzpicture} \\
|
|
||||||
$#2$
|
|
||||||
\end{array}
|
|
||||||
\hfill
|
|
||||||
$
|
|
||||||
}
|
|
||||||
|
|
||||||
\newcommand{\showTwoWriters}[4]
|
|
||||||
{
|
|
||||||
$
|
|
||||||
\begin{array}{cc}
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\clip (0,0) circle (0.2\columnwidth) ;
|
|
||||||
\node[anchor=center] at (0,0) {\includegraphics[width=0.4\columnwidth]{#1}};
|
|
||||||
\end{tikzpicture}
|
|
||||||
&
|
|
||||||
\begin{tikzpicture}
|
|
||||||
\clip (0,0) circle (0.2\columnwidth) ;
|
|
||||||
\node[anchor=center] at (0,0) {\includegraphics[width=0.4\columnwidth]{#3}};
|
|
||||||
\end{tikzpicture} \\
|
|
||||||
$#2$ & $#4$
|
|
||||||
\end{array}
|
|
||||||
$
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# ProostenKrant — Blazor versie
|
||||||
|
# De texlive-installatie zit in een eigen Docker-laag; normale builds
|
||||||
|
# herinstalleren LaTeX dus NIET. Gebruik `clean` met zorg (verwijdert ook de MySQL-data).
|
||||||
|
|
||||||
|
.PHONY: help build up down restart logs logs-app logs-mysql shell mysql clean
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "ProostenKrant (Blazor) development commands"
|
||||||
|
@echo ""
|
||||||
|
@echo " build - Build images en start containers"
|
||||||
|
@echo " up - Start containers"
|
||||||
|
@echo " down - Stop containers"
|
||||||
|
@echo " restart - Restart containers"
|
||||||
|
@echo " logs - Vol alle logs"
|
||||||
|
@echo " logs-app - Vol app logs"
|
||||||
|
@echo " shell - Shell in de app container"
|
||||||
|
@echo " mysql - MySQL shell"
|
||||||
|
@echo " clean - Verwijder containers én volumes (mysql data gaat verloren!)"
|
||||||
|
|
||||||
|
build:
|
||||||
|
docker compose up -d --build
|
||||||
|
@echo "Applicatie draait op http://localhost:8080"
|
||||||
|
|
||||||
|
up:
|
||||||
|
docker compose up -d
|
||||||
|
@echo "Applicatie draait op http://localhost:8080"
|
||||||
|
|
||||||
|
down:
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
restart:
|
||||||
|
docker compose restart
|
||||||
|
|
||||||
|
logs:
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
logs-app:
|
||||||
|
docker compose logs -f app
|
||||||
|
|
||||||
|
shell:
|
||||||
|
docker compose exec app bash
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
docker compose exec mysql mysql -u proostenkrant -pproostenkrant proostenkrant
|
||||||
|
|
||||||
|
clean:
|
||||||
|
docker compose down -v
|
||||||
|
@echo "Containers en volumes verwijderd (ook mysql-data)"
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
|
||||||
VisualStudioVersion = 17.4.33205.214
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Proostenkrant.NET", "Proostenkrant.NET\Proostenkrant.NET.csproj", "{F984715F-2FCA-4A9A-B8A9-020D482FD394}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{F984715F-2FCA-4A9A-B8A9-020D482FD394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{F984715F-2FCA-4A9A-B8A9-020D482FD394}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{F984715F-2FCA-4A9A-B8A9-020D482FD394}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{F984715F-2FCA-4A9A-B8A9-020D482FD394}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {08AC80BF-ECB6-4A5C-8241-1E5F99C8524F}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<configuration>
|
|
||||||
<startup>
|
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
|
|
||||||
</startup>
|
|
||||||
</configuration>
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Proostenkrant.NET
|
|
||||||
{
|
|
||||||
internal static class ArticleGenerator
|
|
||||||
{
|
|
||||||
internal static string GenerateArticle(string[][] rows)
|
|
||||||
{
|
|
||||||
var text = string.Empty;
|
|
||||||
|
|
||||||
foreach (var row in rows)
|
|
||||||
{
|
|
||||||
var writers = row[1].Split(',').Select(w => w.Trim()).ToArray();
|
|
||||||
var title = row[2];
|
|
||||||
var date = row[3];
|
|
||||||
var mainText = row[4];
|
|
||||||
var imageName = row[5];
|
|
||||||
|
|
||||||
text += GenerateHeader(writers[0], title, date);
|
|
||||||
text += GenerateBody(writers, date, mainText, imageName);
|
|
||||||
text += "\n\n";
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
static string GenerateHeader(string writer, string title, string date)
|
|
||||||
{
|
|
||||||
var text = string.Empty;
|
|
||||||
text += "\\headline{" + title + "}\r\n";
|
|
||||||
text += "\\byline{" + date + "}{" + writer + "}\r\n";
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
static string GenerateBody(string[] writers, string date, string mainText, string imageName)
|
|
||||||
{
|
|
||||||
var text = string.Empty;
|
|
||||||
if (writers.Length == 2)
|
|
||||||
text += "\\showTwoWriters{Images/ProfilePictures/" + writers[0] + ".jpg}{" + writers[0] + "}{Images/ProfilePictures/" + writers[1] + ".jpg}{" + writers[1] + "}\r\n";
|
|
||||||
else
|
|
||||||
text += "\\showOneWriter{Images/ProfilePictures/" + writers[0] + ".jpg}{" + writers[0] + "}\r\n";
|
|
||||||
text += "\\par " + mainText + "\r\n";
|
|
||||||
text += "\\showInlineImage{Images/Articles/" + imageName + ".jpg}{" + imageName + "}\r\n";
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
|
|
||||||
namespace Proostenkrant.NET
|
|
||||||
{
|
|
||||||
|
|
||||||
internal static class GoogleFormsReader
|
|
||||||
{
|
|
||||||
const string tsvDataUrl = "https://docs.google.com/spreadsheets/d/e/2PACX-1vRfxfdT8f4MQjXWmZylnXCGl14T0x5mxNw3W1Xd8GrZfPivjcqRGIvmb2YoAf77M00PrGPSJ6Zo-hRs/pub?output=tsv";
|
|
||||||
const string downloadURL = "https://drive.google.com/uc?export=download&id=FILEID";
|
|
||||||
public static string[][] GetData()
|
|
||||||
{
|
|
||||||
var tsvData = GetTsvData();
|
|
||||||
var data = ConvertData(tsvData);
|
|
||||||
var imageNames = LoadImages(data);
|
|
||||||
|
|
||||||
return data.Select((v, i) =>
|
|
||||||
{
|
|
||||||
v[5] = imageNames[i];
|
|
||||||
return v;
|
|
||||||
}).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
static string GetTsvData()
|
|
||||||
{
|
|
||||||
var client = new HttpClient();
|
|
||||||
var downloadTsvTask = client.GetStringAsync(tsvDataUrl);
|
|
||||||
downloadTsvTask.Wait();
|
|
||||||
return downloadTsvTask.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static string[][] ConvertData(string tsvData)
|
|
||||||
{
|
|
||||||
// Split rows
|
|
||||||
var tsvRows = tsvData.Split('\r').Where(v => v.Length > 0);
|
|
||||||
// Split values
|
|
||||||
var rows = tsvRows.Select(r => r.Split('\t').Where(v => v.Length > 0));
|
|
||||||
// Remove header rows
|
|
||||||
rows = rows.ToList().GetRange(2, rows.Count() - 2);
|
|
||||||
|
|
||||||
return rows.Select(r => r.ToArray()).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
static string[] LoadImages(string[][] data)
|
|
||||||
{
|
|
||||||
var names = new List<string>();
|
|
||||||
|
|
||||||
foreach (var row in data)
|
|
||||||
{
|
|
||||||
var name = Regex.Replace(row[2], string.Concat(Path.GetInvalidFileNameChars()), string.Empty, RegexOptions.Compiled);
|
|
||||||
name = name.Replace(":", "");
|
|
||||||
names.Add(name);
|
|
||||||
|
|
||||||
var filename = Program.c_articleImagesPath + $"/{name}.jpg";
|
|
||||||
if (File.Exists(filename)) continue;
|
|
||||||
|
|
||||||
var id = row[5].Replace("https://drive.google.com/open?id=", string.Empty);
|
|
||||||
var url = downloadURL.Replace("FILEID", id);
|
|
||||||
|
|
||||||
Console.WriteLine("Downloading images using the browser...");
|
|
||||||
var process = Process.Start(url);
|
|
||||||
process.WaitForExit();
|
|
||||||
|
|
||||||
Console.Write("Drag image here : ");
|
|
||||||
var path = Console.ReadLine().Trim('\"');
|
|
||||||
File.Copy(path, filename);
|
|
||||||
}
|
|
||||||
return names.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Proostenkrant.NET
|
|
||||||
{
|
|
||||||
internal static class LatexCompiler
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
internal static void Compile(string text)
|
|
||||||
{
|
|
||||||
File.WriteAllText(Program.c_articlePath, text);
|
|
||||||
GeneratePdf();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GeneratePdf()
|
|
||||||
{
|
|
||||||
var processInfo = new ProcessStartInfo("cmd.exe", "/c pdflatex " + Program.c_mainPath);
|
|
||||||
processInfo.WorkingDirectory = Program.c_latexPath;
|
|
||||||
processInfo.CreateNoWindow = true;
|
|
||||||
processInfo.UseShellExecute = true;
|
|
||||||
|
|
||||||
var process = Process.Start(processInfo);
|
|
||||||
process.WaitForExit();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Proostenkrant.NET
|
|
||||||
{
|
|
||||||
public class Program
|
|
||||||
{
|
|
||||||
internal const string c_latexPath = "../Latex";
|
|
||||||
internal const string c_mainPath = c_latexPath + "/Main.tex";
|
|
||||||
internal const string c_articlePath = c_latexPath + "/Articles.tex";
|
|
||||||
internal const string c_imagesPath = c_latexPath + "/Images";
|
|
||||||
internal const string c_articleImagesPath = c_imagesPath + "/Articles";
|
|
||||||
|
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
|
||||||
var rows = GoogleFormsReader.GetData();
|
|
||||||
var text = ArticleGenerator.GenerateArticle(rows);
|
|
||||||
LatexCompiler.Compile(text);
|
|
||||||
|
|
||||||
Console.WriteLine("Done!");
|
|
||||||
Console.ReadLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
|
||||||
<PropertyGroup>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{F984715F-2FCA-4A9A-B8A9-020D482FD394}</ProjectGuid>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<RootNamespace>Proostenkrant.NET</RootNamespace>
|
|
||||||
<AssemblyName>Proostenkrant.NET</AssemblyName>
|
|
||||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
|
||||||
<Deterministic>true</Deterministic>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>..\Build\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
<DocumentationFile>
|
|
||||||
</DocumentationFile>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Drawing" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
|
||||||
<Reference Include="Microsoft.CSharp" />
|
|
||||||
<Reference Include="System.Data" />
|
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="ArticleGenerator.cs" />
|
|
||||||
<Compile Include="GoogleFormsReader.cs" />
|
|
||||||
<Compile Include="LatexCompiler.cs" />
|
|
||||||
<Compile Include="Program.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Include="App.config" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<WCFMetadata Include="Connected Services\" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
|
||||||
</Project>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
|
||||||
// set of attributes. Change these attribute values to modify the information
|
|
||||||
// associated with an assembly.
|
|
||||||
[assembly: AssemblyTitle("Proostenkrant.NET")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
|
||||||
[assembly: AssemblyProduct("Proostenkrant.NET")]
|
|
||||||
[assembly: AssemblyCopyright("Copyright © 2022")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||||
// to COM components. If you need to access a type in this assembly from
|
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||||
[assembly: ComVisible(false)]
|
|
||||||
|
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|
||||||
[assembly: Guid("f984715f-2fca-4a9a-b8a9-020d482fd394")]
|
|
||||||
|
|
||||||
// Version information for an assembly consists of the following four values:
|
|
||||||
//
|
|
||||||
// Major Version
|
|
||||||
// Minor Version
|
|
||||||
// Build Number
|
|
||||||
// Revision
|
|
||||||
//
|
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||||
// by using the '*' as shown below:
|
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
// <autogenerated />
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
36ee40575dbbea1fa5315cbc1554d56b6770af33
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
C:\Users\douwe\Projects\Proostenkrant.NET\Build\Proostenkrant.NET.exe.config
|
|
||||||
C:\Users\douwe\Projects\Proostenkrant.NET\Build\Proostenkrant.NET.exe
|
|
||||||
C:\Users\douwe\Projects\Proostenkrant.NET\Build\Proostenkrant.NET.pdb
|
|
||||||
C:\Users\douwe\Projects\Proostenkrant.NET\Proostenkrant.NET\obj\Debug\Proostenkrant.NET.csproj.AssemblyReference.cache
|
|
||||||
C:\Users\douwe\Projects\Proostenkrant.NET\Proostenkrant.NET\obj\Debug\Proostenkrant.NET.csproj.SuggestedBindingRedirects.cache
|
|
||||||
C:\Users\douwe\Projects\Proostenkrant.NET\Proostenkrant.NET\obj\Debug\Proostenkrant.NET.csproj.CoreCompileInputs.cache
|
|
||||||
C:\Users\douwe\Projects\Proostenkrant.NET\Proostenkrant.NET\obj\Debug\Proostenkrant.NET.exe
|
|
||||||
C:\Users\douwe\Projects\Proostenkrant.NET\Proostenkrant.NET\obj\Debug\Proostenkrant.NET.pdb
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
// <autogenerated />
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
|
|
||||||
@@ -1,2 +1,136 @@
|
|||||||
# ProostenKrant
|
# ProostenKrant 3.0 — Blazor
|
||||||
A newspaper for our family
|
|
||||||
|
De ProostenKrant is een webkrant van en voor de familie Proosten: familieleden leveren
|
||||||
|
artikelen in via de website, de redactie publiceert ze per seizoeneditie en drukt de
|
||||||
|
krant als PDF via LaTeX.
|
||||||
|
|
||||||
|
Deze versie vervangt de PHP/MySQL-applicatie door een **Blazor Server**-applicatie
|
||||||
|
(.NET 10) met dezelfde database en hetzelfde LaTeX-krantenontwerp.
|
||||||
|
|
||||||
|
## Architectuur
|
||||||
|
|
||||||
|
| Onderdeel | Keuze |
|
||||||
|
|---------------|---------------------------------------------------|
|
||||||
|
| Framework | Blazor Web App (static SSR, geen JavaScript nodig) |
|
||||||
|
| Database | MySQL 8 (zelfde schema als voorheen, Dapper) |
|
||||||
|
| Authenticatie | Cookie-auth met universeel wachtwoord + paszin |
|
||||||
|
| PDF-generatie | LaTeX (`newspaper`-pakket) aangeroepen vanuit .NET |
|
||||||
|
| Hosting | Docker Compose: `app` (Blazor + texlive) + `mysql`|
|
||||||
|
|
||||||
|
**Waarom Blazor Server en niet WebAssembly?** De applicatie is servergebonden:
|
||||||
|
MySQL-toegang, bestandsuploads en LaTeX-compilatie gebeuren allemaal op de server.
|
||||||
|
Met WASM zou je een aparte REST-API met token-authenticatie moeten bouwen en dezelfde
|
||||||
|
werkzaamheden toch op de server doen. Server-side rendering is bovendien sneller voor
|
||||||
|
een kleine gebruikersgroep en werkt zonder JavaScript.
|
||||||
|
|
||||||
|
**Waarom LaTeX en niet een .NET PDF-bibliotheek?** .NET kan goede PDF's genereren
|
||||||
|
(bijv. met QuestPDF, gratis Community-licentie), maar echte krantenzetting —
|
||||||
|
automatische tekstverdeling over drie kolommen, microtype, woordafbreking,
|
||||||
|
kopbalancering — is waar TeX in uitblinkt. De krant blijft daarom met LaTeX gedrukt,
|
||||||
|
nu aangestuurd vanuit .NET.
|
||||||
|
|
||||||
|
## Snel starten
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
Daarna:
|
||||||
|
|
||||||
|
- Website: http://localhost:8080
|
||||||
|
- Inloggen redactie: kies je naam + universel wachtwoord (standaard `ProostenKrant2024!`,
|
||||||
|
via `UNIVERSAL_PASSWORD` in `.env`)
|
||||||
|
- Paszin voor inzendingen: hetzelfde wachtwoord
|
||||||
|
|
||||||
|
De eerste build duurt lang (de texlive-installatie is ±2 GB). Daarna cachet Docker de
|
||||||
|
LaTeX-laag: **hertesten vraagt geen herinstallatie meer**, zolang je niet
|
||||||
|
`--no-cache` gebruikt.
|
||||||
|
|
||||||
|
## Gebruik
|
||||||
|
|
||||||
|
| Route | Doel |
|
||||||
|
|------------------------------|---------------------------------------------------|
|
||||||
|
| `/` | Voorpagina (krantenstijl) |
|
||||||
|
| `/indienen` | Artikel inleveren via paszin |
|
||||||
|
| `/indienen/bewerken` | Kladartikel bewerken (alleen de schrijver) |
|
||||||
|
| `/archief` | Gedrukte edities + gepubliceerde artikelen |
|
||||||
|
| `/login` | Redactie-login |
|
||||||
|
| `/dashboard` | Overzicht, statistieken, krant drukken |
|
||||||
|
| `/artikelen`, `/artikelen/nieuw`, `/artikelen/{id}` | Artikelbeheer (redactie) |
|
||||||
|
| `/seizoenen`, `/seizoenen/{id}` | Edities drukken (redactie) |
|
||||||
|
| `/edities/{bestand}` | PDF-download |
|
||||||
|
|
||||||
|
Werkstroom: familie dient in via `/indienen` (status *klad*) → redactie publiceert
|
||||||
|
via `/artikelen/{id}` → redactie drukt de editie via `/seizoenen` of `/dashboard`.
|
||||||
|
|
||||||
|
## Projectstructuur
|
||||||
|
|
||||||
|
```
|
||||||
|
proostenkrant.net/
|
||||||
|
├── src/ProostenKrant/ # Blazor-applicatie
|
||||||
|
│ ├── Components/Pages/ # Alle pagina's (Razor, static SSR)
|
||||||
|
│ ├── Services/ # Db, ArticleService, LatexService, auth, uploads
|
||||||
|
│ └── wwwroot/ # css, fonts, EU-vlag (resources volume-mounted)
|
||||||
|
├── deploy/
|
||||||
|
│ ├── Dockerfile # Build + runtime met texlive (gecachete laag)
|
||||||
|
│ └── mysql/init.sql # Database-initialisatie
|
||||||
|
├── resources/
|
||||||
|
│ ├── archive/ # Gedrukte edities (PDF's)
|
||||||
|
│ ├── images/
|
||||||
|
│ │ ├── profiles/ # Profielfoto's (naam = weergavenaam)
|
||||||
|
│ │ └── articles/ # Artikelafbeeldingen (via website geüpload)
|
||||||
|
│ └── templates/latex/ # LaTeX-ontwerp ter referentie
|
||||||
|
└── compose.yml # app + mysql stack
|
||||||
|
```
|
||||||
|
|
||||||
|
De volledige resources-map wordt via volumes aan de app gekoppeld:
|
||||||
|
|
||||||
|
- `resources/images` → `wwwroot/resources/images` — profielfoto's en
|
||||||
|
artikelafbeeldingen. Een profielfoto hoort bij een familielid als de
|
||||||
|
bestandsnaam (zonder extensie) overeenkomt met de weergavenaam
|
||||||
|
(`Geert.jpg` → Geert). Een later toegevoegde foto wordt dus automatisch
|
||||||
|
gekoppeld; een verwijderde foto valt terug op `Unknown.jpg`.
|
||||||
|
- `resources/archive` → `/app/archive` — de bestaande archief-edities
|
||||||
|
worden getoond en pas gedrukte kranten worden hier bewaard.
|
||||||
|
|
||||||
|
De Europese vlag in de voettekst is een SVG (`src/ProostenKrant/wwwroot/images/european-flag.svg`)
|
||||||
|
volgens de officiële specificaties (12 gouden sterren, cirkelradius ⅓ van de hoogte).
|
||||||
|
|
||||||
|
## Deployen op een server
|
||||||
|
|
||||||
|
1. Vereiste: Docker + Docker Compose op de server.
|
||||||
|
2. Clone de repository en zet in `.env` een **sterk wachtwoord** voordat je live gaat:
|
||||||
|
- `UNIVERSAL_PASSWORD` — inlogwachtwoord én paszin (standaard `ProostenKrant2024!`)
|
||||||
|
- `DB_PASSWORD` / `MYSQL_ROOT_PASSWORD` — database (`MYSQL_ROOT_PASSWORD` staat in `compose.yml`)
|
||||||
|
3. Start: `docker compose up -d --build` — de database wordt automatisch geïnitialiseerd
|
||||||
|
(users incl. Kevin, Luc en Tilde; seizoenen).
|
||||||
|
4. Zet eventueel een reverse proxy met HTTPS (Caddy/nginx) voor `http://server:8080`.
|
||||||
|
Overweeg de MySQL-poort (`3306`) dicht te zetten in `compose.yml` als de database
|
||||||
|
niet van buitenaf benodigd is.
|
||||||
|
5. Backups: de `resources/`-map (foto's, artikelafbeeldingen, archief-PDF's) en
|
||||||
|
de mysql-data volume bevatten alles; `docker compose down -v` verwijdert de
|
||||||
|
data definitief.
|
||||||
|
|
||||||
|
De eerste build installeert texlive eenmalig (±2 GB, enkele minuten); daarna pakken
|
||||||
|
Docker-lagen alles en duurt een rebuild seconden.
|
||||||
|
|
||||||
|
## Configuratie
|
||||||
|
|
||||||
|
Omgevingsvariabelen (of `.env`):
|
||||||
|
|
||||||
|
- `DB_HOST`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` — database (standaard `mysql`/`proostenkrant`)
|
||||||
|
- `UNIVERSAL_PASSWORD` — inlogwachtwoord en paszin (standaard `ProostenKrant2024!`)
|
||||||
|
|
||||||
|
Paden (standaarden in `appsettings.json`, sectie `Paths`):
|
||||||
|
|
||||||
|
- `StaticArchive` / `LatexOutput` — map voor archief-edities én pas gedrukte
|
||||||
|
kranten (`archive`, gekoppeld aan `resources/archive`)
|
||||||
|
- `ResourceImages` — map voor afbeeldingen uit de resources (`resources/images`,
|
||||||
|
gekoppeld aan `resources/images`; submappen `profiles` en `articles`)
|
||||||
|
|
||||||
|
## Legacy PHP-versie
|
||||||
|
|
||||||
|
De oude PHP-applicatie is opgeruimd; de foto's en archief-PDF's zijn verplaatst
|
||||||
|
naar de `resources/`-map. Alleen de oude `docker/`-map (nginx/php-config van de
|
||||||
|
PHP-versie) staat nog in de repo en kan verwijderd worden — de Blazor-stack
|
||||||
|
gebruikt enkel `compose.yml` en `deploy/Dockerfile`.
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
# ProostenKrant — Blazor Server stack
|
||||||
|
# Gebruik: docker compose up -d --build
|
||||||
|
# App: http://localhost:8080
|
||||||
|
|
||||||
|
name: proostenkrant
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: deploy/Dockerfile
|
||||||
|
container_name: proostenkrant-app
|
||||||
|
ports:
|
||||||
|
- "8050:8080"
|
||||||
|
environment:
|
||||||
|
DB_HOST: mysql
|
||||||
|
DB_NAME: ${DB_NAME:-proostenkrant}
|
||||||
|
DB_USER: ${DB_USER:-proostenkrant}
|
||||||
|
DB_PASSWORD: ${DB_PASSWORD:-proostenkrant}
|
||||||
|
UNIVERSAL_PASSWORD: ${UNIVERSAL_PASSWORD:-ProostenKrant2024!}
|
||||||
|
HOOGTES_BEWAREN: ${HOOGTES_BEWAREN:-0}
|
||||||
|
volumes:
|
||||||
|
# Foto's en afbeeldingen uit de resources-map: profielfoto's
|
||||||
|
# (resources/images/profiles) en artikeluploads (resources/images/articles)
|
||||||
|
- ./resources/images:/app/wwwroot/resources/images
|
||||||
|
# Archief-edities: bestaande PDF's tonen én pas gedrukte kranten
|
||||||
|
# worden hier bewaard (resources/archive)
|
||||||
|
- ./resources/archive:/app/archive
|
||||||
|
# DataProtection-sleutels (versleuteling van login-cookies en
|
||||||
|
# antiforgery-tokens). Zonder dit volume worden alle sessies
|
||||||
|
# ongeldig bij elke herdeploy van de app-container.
|
||||||
|
- app-dataprotection:/root/.aspnet/DataProtection-Keys
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
container_name: proostenkrant-mysql
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: rootpassword
|
||||||
|
MYSQL_DATABASE: ${DB_NAME:-proostenkrant}
|
||||||
|
MYSQL_USER: ${DB_USER:-proostenkrant}
|
||||||
|
MYSQL_PASSWORD: ${DB_PASSWORD:-proostenkrant}
|
||||||
|
volumes:
|
||||||
|
- mysql-data:/var/lib/mysql
|
||||||
|
- ./deploy/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-prootpassword"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 20
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mysql-data:
|
||||||
|
app-dataprotection:
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# ProostenKrant — Blazor Server + LaTeX
|
||||||
|
# De texlive-installatie staat in een eigen, vroege laag van de runtime-image,
|
||||||
|
# zodat app-wijzigingen de (trage) LaTeX-installatie niet opnieuw triggeren.
|
||||||
|
|
||||||
|
# ---------- Build ----------
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY src/ProostenKrant/ProostenKrant.csproj ./ProostenKrant/
|
||||||
|
RUN dotnet restore ProostenKrant/ProostenKrant.csproj
|
||||||
|
|
||||||
|
COPY src/ProostenKrant/ ./ProostenKrant/
|
||||||
|
RUN dotnet publish ProostenKrant/ProostenKrant.csproj -c Release -o /app/publish
|
||||||
|
|
||||||
|
# ---------- Runtime ----------
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0
|
||||||
|
|
||||||
|
# LaTeX (eenmalig geinstalleerd, daarna door Docker-laagcache stabiel)
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
texlive-latex-base \
|
||||||
|
texlive-latex-recommended \
|
||||||
|
texlive-latex-extra \
|
||||||
|
texlive-fonts-recommended \
|
||||||
|
texlive-fonts-extra \
|
||||||
|
texlive-publishers \
|
||||||
|
texlive-pictures \
|
||||||
|
texlive-lang-european \
|
||||||
|
ghostscript \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# binhex.tex (nodig voor newtx) zit in texlive-plain-generic.
|
||||||
|
# Aparte laag: wijzigingen hierboven herinstalleren de grote texlive-laag niet.
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
texlive-plain-generic \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV ASPNETCORE_URLS=http://+:8080 \
|
||||||
|
ASPNETCORE_ENVIRONMENT=Production
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/publish .
|
||||||
|
|
||||||
|
RUN mkdir -p /app/wwwroot/resources/images/profiles /app/wwwroot/resources/images/articles /app/archive
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
ENTRYPOINT ["dotnet", "ProostenKrant.dll"]
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
-- ProostenKrant Database Schema
|
||||||
|
-- Dutch Family Newspaper System
|
||||||
|
|
||||||
|
CREATE DATABASE IF NOT EXISTS proostenkrant;
|
||||||
|
USE proostenkrant;
|
||||||
|
|
||||||
|
-- Users table (family members)
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
username VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
display_name VARCHAR(100) NOT NULL,
|
||||||
|
profile_image VARCHAR(255),
|
||||||
|
is_active BOOLEAN DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Seasons table
|
||||||
|
CREATE TABLE IF NOT EXISTS seasons (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
name VARCHAR(50) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
start_date DATE NOT NULL,
|
||||||
|
end_date DATE NOT NULL,
|
||||||
|
is_active BOOLEAN DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Articles table
|
||||||
|
CREATE TABLE IF NOT EXISTS articles (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
title VARCHAR(255) NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
author_id INT NOT NULL,
|
||||||
|
participants TEXT, -- Comma-separated list of user IDs
|
||||||
|
event_date DATE NOT NULL,
|
||||||
|
season_id INT,
|
||||||
|
article_image VARCHAR(255),
|
||||||
|
status ENUM('draft', 'published', 'archived') DEFAULT 'draft',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (season_id) REFERENCES seasons(id) ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- System settings table
|
||||||
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
key_name VARCHAR(100) NOT NULL UNIQUE,
|
||||||
|
value TEXT,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Insert default seasons
|
||||||
|
INSERT INTO seasons (name, description, start_date, end_date) VALUES
|
||||||
|
('Lente', 'Lente editie - Voorjaar', '2026-03-20', '2026-06-20'),
|
||||||
|
('Zomer', 'Zomer editie', '2026-06-21', '2026-09-22'),
|
||||||
|
('Herfst', 'Herfst editie', '2026-09-23', '2026-12-20'),
|
||||||
|
('Winter', 'Winter editie', '2026-12-21', '2026-03-19');
|
||||||
|
|
||||||
|
-- Insert default users (family members)
|
||||||
|
INSERT INTO users (username, display_name, profile_image) VALUES
|
||||||
|
('douwe', 'Douwe', NULL),
|
||||||
|
('pauline', 'Pauline', NULL),
|
||||||
|
('diede', 'Diede', NULL),
|
||||||
|
('dien', 'Dien', NULL),
|
||||||
|
('chris', 'Chris', NULL),
|
||||||
|
('geert', 'Geert', NULL),
|
||||||
|
('annick', 'Annick', NULL),
|
||||||
|
('cedric', 'Cedric', NULL),
|
||||||
|
('gunter', 'Gunter', NULL),
|
||||||
|
('daan', 'Daan', NULL),
|
||||||
|
('kyala', 'Kyala', NULL),
|
||||||
|
('michel', 'Michel', NULL),
|
||||||
|
('dries', 'Dries', NULL),
|
||||||
|
('pieter', 'Pieter', NULL),
|
||||||
|
('mathilde', 'Mathilde', NULL),
|
||||||
|
('kevin', 'Kevin', NULL),
|
||||||
|
('luc', 'Luc', NULL),
|
||||||
|
('tilde', 'Tilde', NULL),
|
||||||
|
('mattis', 'Mattis', NULL);
|
||||||
|
|
||||||
|
-- Insert universal password setting
|
||||||
|
INSERT INTO settings (key_name, value) VALUES
|
||||||
|
('universal_password', 'ProostenKrant2024!');
|
||||||
|
|
||||||
|
-- Create indexes for better performance
|
||||||
|
CREATE INDEX idx_articles_author ON articles(author_id);
|
||||||
|
CREATE INDEX idx_articles_season ON articles(season_id);
|
||||||
|
CREATE INDEX idx_articles_date ON articles(event_date);
|
||||||
|
CREATE INDEX idx_articles_status ON articles(status);
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
FROM texlive/texlive:latest
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
wget \
|
||||||
|
ghostscript \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /var/www
|
||||||
|
|
||||||
|
# LaTeX is already installed in the texlive image
|
||||||
|
# No need to install additional packages
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["/bin/bash"]
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
FROM nginx:alpine
|
||||||
|
|
||||||
|
RUN apk add --no-cache curl
|
||||||
|
|
||||||
|
COPY docker/nginx/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name localhost;
|
||||||
|
root /var/www/html/public;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
fastcgi_pass php:9000;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
FROM php:8.2-fpm-alpine
|
||||||
|
|
||||||
|
# Install dependencies for GD extension
|
||||||
|
RUN apk add --no-cache --virtual .build-deps \
|
||||||
|
freetype-dev \
|
||||||
|
libjpeg-turbo-dev \
|
||||||
|
libpng-dev \
|
||||||
|
libwebp-dev \
|
||||||
|
zlib-dev
|
||||||
|
|
||||||
|
# Install and enable GD extension with all image format support
|
||||||
|
# Note: --with-png and --with-jpeg are now built-in, only need freetype and webp
|
||||||
|
RUN docker-php-ext-configure gd \
|
||||||
|
--with-freetype \
|
||||||
|
--with-webp
|
||||||
|
|
||||||
|
# Install PHP extensions
|
||||||
|
RUN docker-php-ext-install -j$(nproc) \
|
||||||
|
gd \
|
||||||
|
mysqli \
|
||||||
|
pdo \
|
||||||
|
pdo_mysql
|
||||||
|
|
||||||
|
# Clean up build dependencies
|
||||||
|
RUN apk del .build-deps
|
||||||
|
|
||||||
|
# Also install runtime libraries for GD
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
freetype \
|
||||||
|
libjpeg-turbo \
|
||||||
|
libpng \
|
||||||
|
libwebp \
|
||||||
|
zlib
|
||||||
|
|
||||||
|
# Copy PHP configuration
|
||||||
|
COPY docker/php/php.ini /usr/local/etc/php/conf.d/custom.ini
|
||||||
|
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
|
EXPOSE 9000
|
||||||
|
|
||||||
|
CMD ["php-fpm"]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
upload_max_filesize = 50M
|
||||||
|
post_max_size = 52M
|
||||||
|
memory_limit = 512M
|
||||||
|
max_execution_time = 300
|
||||||
|
display_errors = On
|
||||||
|
error_reporting = E_ALL
|
||||||
|
date.timezone = Europe/Brussels
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
\documentclass[10pt]{article}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage[T1]{fontenc}
|
||||||
|
\usepackage{microtype}
|
||||||
|
\usepackage[dutch]{babel}
|
||||||
|
\usepackage{newspaper}
|
||||||
|
\usepackage[letterpaper,left=36pt,right=36pt,top=0.55in,bottom=36pt,headheight=40pt,headsep=12pt]{geometry}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{tikz}
|
||||||
|
\usepackage[export]{adjustbox}
|
||||||
|
\usepackage{calc}
|
||||||
|
\usepackage{newtxtext}
|
||||||
|
|
||||||
|
\date{\today}
|
||||||
|
\currentvolume{1}
|
||||||
|
\currentissue{1}
|
||||||
|
\SetPaperName{Proo{s:}tenKrant}
|
||||||
|
\SetHeaderName{ProostenKrant}
|
||||||
|
\SetPaperLocation{Regio Kempen}
|
||||||
|
\SetPaperSlogan{``Een krant van en voor Proosten.''}
|
||||||
|
\SetPaperPrice{NUL EURO}
|
||||||
|
|
||||||
|
\newcommand{\blocksep}{\par\nointerlineskip\vspace{11pt}\noindent{\rule{\linewidth}{0.4pt}}\par\vspace{11pt}}
|
||||||
|
\newcommand{\colrule}{\hfill{\vrule width 0.3pt}\hfill}
|
||||||
|
\begin{document}
|
||||||
|
\maketitle
|
||||||
|
\noindent\textbf{\LARGE Editie: Herfst}\par
|
||||||
|
\vspace{8pt}
|
||||||
|
|
||||||
|
\expandafter\newsavebox\csname blk1W\endcsname
|
||||||
|
\expandafter\savebox\csname blk1W\endcsname{\parbox{356.0pt}{{\noindent{\LARGE\itshape De Proostenkrant is back baby!!!}\par
|
||||||
|
\vspace{3pt}{\footnotesize\scshape 9 oktober 2026 --- Douwe}\par
|
||||||
|
\vspace{6pt}\noindent
|
||||||
|
\begin{minipage}[t]{0.63\linewidth}
|
||||||
|
\vspace*{0pt}Whoehoe! We voelde allemaal dat er iets miste in ons leven de laatste tijd. Sommige dachten dat dat een McDonalds in Schoonbroek was, andere dachten dat ze gewoon naar de andere kant van de wereld moesten reizen voor onbepaalde duur, maar gelukkig weet ik echt wat er mist. De 4 jaarlijkse editie van de ProostenKrant. Met men eigen server-infrastructuur ter Turnhout kan ik nu lekker gemakkelijk zelf de ProostenKrant website hosten en kunnen jullie allemaal nog gemakkelijker artikels indienen. Dus aan allen der Proosten, verdeel en rapporteer u!\par
|
||||||
|
\end{minipage}
|
||||||
|
\hfill
|
||||||
|
\begin{minipage}[t]{0.34\linewidth}
|
||||||
|
\vspace*{0pt}\setlength{\fboxsep}{3pt}\framebox{\includegraphics[max width=\dimexpr\linewidth-2\fboxsep-2\fboxrule\relax,max height=0.26\textheight,keepaspectratio]{/app/wwwroot/resources/images/articles/05568e0b_ScreenshotFrom20261009133307.png}}\par
|
||||||
|
\end{minipage}
|
||||||
|
\par\vspace{6pt}\centering\begin{minipage}[c]{103.0pt}\centering\begin{tikzpicture}\clip (0,0) circle (44.5pt);\node at (0,0) {\includegraphics[width=89.0pt,height=89.0pt,keepaspectratio]{/app/wwwroot/resources/images/profiles/Douwe.jpg}};\end{tikzpicture}\par\vspace{2pt}{\footnotesize Douwe}\end{minipage}\par
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
\noindent
|
||||||
|
\begin{minipage}[t]{356.0pt}
|
||||||
|
\expandafter\usebox\csname blk1W\endcsname\par\vspace{14pt}
|
||||||
|
\noindent
|
||||||
|
\begin{minipage}[t]{172.0pt}
|
||||||
|
\vspace*{0pt}
|
||||||
|
\end{minipage}\colrule
|
||||||
|
\begin{minipage}[t]{172.0pt}
|
||||||
|
\vspace*{0pt}
|
||||||
|
\end{minipage}
|
||||||
|
\end{minipage}\colrule
|
||||||
|
\begin{minipage}[t]{172.0pt}
|
||||||
|
\vspace*{0pt}
|
||||||
|
\end{minipage}
|
||||||
|
\end{document}
|
||||||
|
Before Width: | Height: | Size: 339 KiB After Width: | Height: | Size: 339 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 733 KiB After Width: | Height: | Size: 733 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 482 KiB After Width: | Height: | Size: 482 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 745 KiB After Width: | Height: | Size: 745 KiB |
|
Before Width: | Height: | Size: 269 KiB After Width: | Height: | Size: 269 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@@ -30,20 +30,24 @@
|
|||||||
\usepackage{graphicx}
|
\usepackage{graphicx}
|
||||||
\usepackage{tikz}
|
\usepackage{tikz}
|
||||||
\usepackage{multicol}
|
\usepackage{multicol}
|
||||||
\usepackage{subfiles}
|
\usepackage{geometry}
|
||||||
\usepackage{newspaper-mod}
|
\usepackage{array}
|
||||||
|
\usepackage{wrapfig}
|
||||||
|
\usepackage{lipsum}
|
||||||
|
\usepackage{newtxtext,newtxmath}
|
||||||
|
\usepackage{etoolbox}
|
||||||
|
|
||||||
|
% Custom styling for better image handling
|
||||||
|
\input{newspaper-mod}
|
||||||
|
|
||||||
%%%%%%%%% Front matter %%%%%%%%%%
|
%%%%%%%%% Front matter %%%%%%%%%%
|
||||||
|
|
||||||
\usepackage{lipsum}
|
|
||||||
|
|
||||||
\begin{document}
|
\begin{document}
|
||||||
\maketitle
|
\maketitle
|
||||||
|
|
||||||
\begin{multicols}{3}
|
\begin{multicols}{3}
|
||||||
|
|
||||||
\subfile{Articles}
|
% Articles will be inserted here by the PHP generator
|
||||||
|
|
||||||
\newpage
|
\newpage
|
||||||
\end{multicols}
|
\end{multicols}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
% Improved newspaper-mod.sty for ProostenKrant
|
||||||
|
% Better image handling for different formats and sizes
|
||||||
|
|
||||||
|
\newcommand{\headlinestyle}{\itshape\huge}
|
||||||
|
\newcommand{\bylinestyle}{\scshape\Large}
|
||||||
|
\patchcmd{\headline}{#1}{\headlinestyle #1}{}{}
|
||||||
|
\patchcmd{\byline}{#1}{\bylinestyle #1}{}{}
|
||||||
|
|
||||||
|
% Improved image commands with better handling of different aspect ratios
|
||||||
|
|
||||||
|
% Show inline image with caption (automatically handles landscape and portrait)
|
||||||
|
\newcommand{\showInlineImage}[2]
|
||||||
|
{
|
||||||
|
\\\n \hspace*{0pt}% Ensure proper spacing
|
||||||
|
\vspace*{10pt}% Add space before image
|
||||||
|
\begin{center}
|
||||||
|
\begin{minipage}{0.95\columnwidth}
|
||||||
|
\centering
|
||||||
|
% Use max dimensions that fit in column
|
||||||
|
\setlength{\fboxsep}{5pt}
|
||||||
|
\framebox{
|
||||||
|
% Check image dimensions and scale appropriately
|
||||||
|
\includegraphics[max width=\linewidth, max height=0.6\textheight, keepaspectratio]{#1}
|
||||||
|
}
|
||||||
|
\vspace*{5pt}
|
||||||
|
{\small #2}
|
||||||
|
\end{minipage}
|
||||||
|
\end{center}
|
||||||
|
\vspace*{10pt}% Add space after image
|
||||||
|
\par
|
||||||
|
}
|
||||||
|
|
||||||
|
% Show single writer with circular profile image
|
||||||
|
\newcommand{\showOneWriter}[2]
|
||||||
|
{
|
||||||
|
\\\n \hfill
|
||||||
|
\begin{minipage}[t]{0.4\columnwidth}
|
||||||
|
\centering
|
||||||
|
\begin{tikzpicture}
|
||||||
|
% Circular clip for profile image
|
||||||
|
\clip (0,0) circle (1.3cm);
|
||||||
|
% Use square aspect ratio and scale to fit
|
||||||
|
\node[anchor=center] at (0,0) {
|
||||||
|
\includegraphics[width=2.8cm, height=2.8cm, keepaspectratio]{#1}
|
||||||
|
};
|
||||||
|
\end{tikzpicture}
|
||||||
|
\vspace*{5pt}
|
||||||
|
{\small #2}
|
||||||
|
\end{minipage}
|
||||||
|
\hfill
|
||||||
|
\\\n}
|
||||||
|
|
||||||
|
% Show two writers with circular profile images
|
||||||
|
\newcommand{\showTwoWriters}[4]
|
||||||
|
{
|
||||||
|
\\\n \begin{center}
|
||||||
|
\begin{minipage}[t]{0.8\columnwidth}
|
||||||
|
\centering
|
||||||
|
\begin{tikzpicture}
|
||||||
|
\clip (0,0) circle (1.1cm);
|
||||||
|
\node[anchor=west] at (0,0) {
|
||||||
|
\includegraphics[width=2.4cm, height=2.4cm, keepaspectratio]{#1}
|
||||||
|
};
|
||||||
|
\end{tikzpicture}
|
||||||
|
\hspace*{15pt}
|
||||||
|
\begin{tikzpicture}
|
||||||
|
\clip (0,0) circle (1.1cm);
|
||||||
|
\node[anchor=east] at (0,0) {
|
||||||
|
\includegraphics[width=2.4cm, height=2.4cm, keepaspectratio]{#3}
|
||||||
|
};
|
||||||
|
\end{tikzpicture}
|
||||||
|
\vspace*{8pt}
|
||||||
|
{\small #2 \\quad #4}
|
||||||
|
\end{minipage}
|
||||||
|
\end{center}
|
||||||
|
\\\n}
|
||||||
|
|
||||||
|
% Alternative commands for different layouts
|
||||||
|
|
||||||
|
% For portrait images - maintains aspect ratio
|
||||||
|
\newcommand{\showPortraitImage}[2]
|
||||||
|
{
|
||||||
|
\\\n \begin{minipage}[t]{0.45\columnwidth}
|
||||||
|
\centering
|
||||||
|
\begin{tikzpicture}
|
||||||
|
% For portrait images, use vertical layout
|
||||||
|
\clip (0,-1.5cm) rectangle (2cm,1.5cm);
|
||||||
|
\node[anchor=center] at (1cm,0) {
|
||||||
|
\includegraphics[height=3cm, keepaspectratio]{#1}
|
||||||
|
};
|
||||||
|
\end{tikzpicture}
|
||||||
|
\vspace*{5pt}
|
||||||
|
{\small #2}
|
||||||
|
\end{minipage}
|
||||||
|
\\\n}
|
||||||
|
|
||||||
|
% For landscape images - maintains aspect ratio
|
||||||
|
\newcommand{\showLandscapeImage}[2]
|
||||||
|
{
|
||||||
|
\\\n \begin{minipage}[t]{\columnwidth}
|
||||||
|
\centering
|
||||||
|
% For landscape images, use full width
|
||||||
|
\includegraphics[width=\linewidth, height=0.4\textheight, keepaspectratio]{#1}
|
||||||
|
\vspace*{5pt}
|
||||||
|
{\small #2}
|
||||||
|
\end{minipage}
|
||||||
|
\\\n}
|
||||||
|
|
||||||
|
% Flexible image wrapper that automatically handles different orientations
|
||||||
|
\newcommand{\showFlexibleImage}[2]
|
||||||
|
{
|
||||||
|
% First, we need to check the image dimensions
|
||||||
|
% This requires the epstopdf package and shell-escape
|
||||||
|
\IfFileExists{#1}{
|
||||||
|
% Image exists, try to determine orientation
|
||||||
|
% For now, we'll use a balanced approach that works for both
|
||||||
|
\showInlineImage{#1}{#2}
|
||||||
|
}{}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
\documentclass[10pt]{article}
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage[T1]{fontenc}
|
||||||
|
\usepackage{microtype}
|
||||||
|
\usepackage[dutch]{babel}
|
||||||
|
\usepackage{newspaper}
|
||||||
|
\usepackage[letterpaper,left=36pt,right=36pt,top=0.55in,bottom=36pt,headheight=40pt,headsep=12pt]{geometry}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
\usepackage{tikz}
|
||||||
|
\usepackage[export]{adjustbox}
|
||||||
|
\usepackage{calc}
|
||||||
|
\usepackage{newtxtext}
|
||||||
|
|
||||||
|
\date{\today}
|
||||||
|
\currentvolume{1}
|
||||||
|
\currentissue{1}
|
||||||
|
\SetPaperName{Proo{s:}tenKrant}
|
||||||
|
\SetHeaderName{ProostenKrant}
|
||||||
|
\SetPaperLocation{Regio Kempen}
|
||||||
|
\SetPaperSlogan{``Een krant van en voor Proosten.''}
|
||||||
|
\SetPaperPrice{NUL EURO}
|
||||||
|
|
||||||
|
\newcommand{\blocksep}{\par\nointerlineskip\vspace{11pt}\noindent{\rule{\linewidth}{0.4pt}}\par\vspace{11pt}}
|
||||||
|
\newcommand{\colrule}{\hfill{\vrule width 0.3pt}\hfill}
|
||||||
|
\newwrite\hoogtes
|
||||||
|
\immediate\openout\hoogtes=hoogtes.txt
|
||||||
|
\begin{document}
|
||||||
|
\maketitle
|
||||||
|
\noindent\textbf{\LARGE Editie: Herfst}\par
|
||||||
|
\vspace{8pt}
|
||||||
|
\pdfsavepos
|
||||||
|
\newpage
|
||||||
|
\immediate\write\hoogtes{MASTHEAD \the\pdflastypos}
|
||||||
|
\immediate\write\hoogtes{GEOM \the\textwidth,\the\textheight}
|
||||||
|
|
||||||
|
\expandafter\newsavebox\csname blk1V\endcsname
|
||||||
|
\expandafter\savebox\csname blk1V\endcsname{\parbox{172.0pt}{{\noindent{\large\itshape De Proostenkrant is back baby!!!}\par
|
||||||
|
\vspace{3pt}{\footnotesize\scshape 9 oktober 2026 --- Douwe}\par
|
||||||
|
\vspace{5pt}Whoehoe! We voelde allemaal dat er iets miste in ons leven de laatste tijd. Sommige dachten dat dat een McDonalds in Schoonbroek was, andere dachten dat ze gewoon naar de andere kant van de wereld moesten reizen voor onbepaalde duur, maar gelukkig weet ik echt wat er mist. De 4 jaarlijkse editie van de ProostenKrant. Met men eigen server-infrastructuur ter Turnhout kan ik nu lekker gemakkelijk zelf de ProostenKrant website hosten en kunnen jullie allemaal nog gemakkelijker artikels indienen. \par Dus aan allen der Proosten, verdeel en rapporteer u!\par
|
||||||
|
\vspace{5pt}\centering\begin{minipage}[c]{57.0pt}\centering\begin{tikzpicture}\clip (0,0) circle (21.5pt);\node at (0,0) {\includegraphics[width=43.0pt,height=43.0pt,keepaspectratio]{/app/wwwroot/resources/images/profiles/Douwe.jpg}};\end{tikzpicture}\par\vspace{2pt}{\footnotesize Douwe}\end{minipage}\par
|
||||||
|
\vspace{5pt}\setlength{\fboxsep}{3pt}\framebox{\includegraphics[max width=\dimexpr\linewidth-2\fboxsep-2\fboxrule\relax,max height=0.22\textheight,keepaspectratio]{/app/wwwroot/resources/images/articles/05568e0b_ScreenshotFrom20261009133307.png}}\par
|
||||||
|
}}}
|
||||||
|
\setlength{\dimen0}{\ht\csname blk1V\endcsname}%
|
||||||
|
\advance\dimen0 by \dp\csname blk1V\endcsname
|
||||||
|
\immediate\write\hoogtes{H 1 V \the\dimen0}
|
||||||
|
|
||||||
|
\expandafter\newsavebox\csname blk1H\endcsname
|
||||||
|
\expandafter\savebox\csname blk1H\endcsname{\parbox{172.0pt}{{\noindent
|
||||||
|
\begin{minipage}[t]{0.63\linewidth}
|
||||||
|
\vspace*{0pt}\noindent{\large\itshape De Proostenkrant is back baby!!!}\par
|
||||||
|
\vspace{3pt}{\footnotesize\scshape 9 oktober 2026 --- Douwe}\par
|
||||||
|
\vspace{5pt}Whoehoe! We voelde allemaal dat er iets miste in ons leven de laatste tijd. Sommige dachten dat dat een McDonalds in Schoonbroek was, andere dachten dat ze gewoon naar de andere kant van de wereld moesten reizen voor onbepaalde duur, maar gelukkig weet ik echt wat er mist. De 4 jaarlijkse editie van de ProostenKrant. Met men eigen server-infrastructuur ter Turnhout kan ik nu lekker gemakkelijk zelf de ProostenKrant website hosten en kunnen jullie allemaal nog gemakkelijker artikels indienen. \par Dus aan allen der Proosten, verdeel en rapporteer u!\par
|
||||||
|
\end{minipage}
|
||||||
|
\hfill
|
||||||
|
\begin{minipage}[t]{0.34\linewidth}
|
||||||
|
\vspace*{0pt}\setlength{\fboxsep}{3pt}\framebox{\includegraphics[max width=\dimexpr\linewidth-2\fboxsep-2\fboxrule\relax,max height=0.30\textheight,keepaspectratio]{/app/wwwroot/resources/images/articles/05568e0b_ScreenshotFrom20261009133307.png}}\par
|
||||||
|
\end{minipage}
|
||||||
|
\par\vspace{5pt}\centering\begin{minipage}[c]{57.0pt}\centering\begin{tikzpicture}\clip (0,0) circle (21.5pt);\node at (0,0) {\includegraphics[width=43.0pt,height=43.0pt,keepaspectratio]{/app/wwwroot/resources/images/profiles/Douwe.jpg}};\end{tikzpicture}\par\vspace{2pt}{\footnotesize Douwe}\end{minipage}\par
|
||||||
|
}}}
|
||||||
|
\setlength{\dimen0}{\ht\csname blk1H\endcsname}%
|
||||||
|
\advance\dimen0 by \dp\csname blk1H\endcsname
|
||||||
|
\immediate\write\hoogtes{H 1 H \the\dimen0}
|
||||||
|
|
||||||
|
\expandafter\newsavebox\csname blk1W\endcsname
|
||||||
|
\expandafter\savebox\csname blk1W\endcsname{\parbox{356.0pt}{{\noindent{\LARGE\itshape De Proostenkrant is back baby!!!}\par
|
||||||
|
\vspace{3pt}{\footnotesize\scshape 9 oktober 2026 --- Douwe}\par
|
||||||
|
\vspace{6pt}\noindent
|
||||||
|
\begin{minipage}[t]{0.63\linewidth}
|
||||||
|
\vspace*{0pt}Whoehoe! We voelde allemaal dat er iets miste in ons leven de laatste tijd. Sommige dachten dat dat een McDonalds in Schoonbroek was, andere dachten dat ze gewoon naar de andere kant van de wereld moesten reizen voor onbepaalde duur, maar gelukkig weet ik echt wat er mist. De 4 jaarlijkse editie van de ProostenKrant. Met men eigen server-infrastructuur ter Turnhout kan ik nu lekker gemakkelijk zelf de ProostenKrant website hosten en kunnen jullie allemaal nog gemakkelijker artikels indienen. \par Dus aan allen der Proosten, verdeel en rapporteer u!\par
|
||||||
|
\end{minipage}
|
||||||
|
\hfill
|
||||||
|
\begin{minipage}[t]{0.34\linewidth}
|
||||||
|
\vspace*{0pt}\setlength{\fboxsep}{3pt}\framebox{\includegraphics[max width=\dimexpr\linewidth-2\fboxsep-2\fboxrule\relax,max height=0.26\textheight,keepaspectratio]{/app/wwwroot/resources/images/articles/05568e0b_ScreenshotFrom20261009133307.png}}\par
|
||||||
|
\end{minipage}
|
||||||
|
\par\vspace{6pt}\centering\begin{minipage}[c]{103.0pt}\centering\begin{tikzpicture}\clip (0,0) circle (44.5pt);\node at (0,0) {\includegraphics[width=89.0pt,height=89.0pt,keepaspectratio]{/app/wwwroot/resources/images/profiles/Douwe.jpg}};\end{tikzpicture}\par\vspace{2pt}{\footnotesize Douwe}\end{minipage}\par
|
||||||
|
}}}
|
||||||
|
\setlength{\dimen0}{\ht\csname blk1W\endcsname}%
|
||||||
|
\advance\dimen0 by \dp\csname blk1W\endcsname
|
||||||
|
\immediate\write\hoogtes{H 1 W \the\dimen0}
|
||||||
|
\end{document}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="nl">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<base href="/" />
|
||||||
|
<link rel="icon" href="images/logo.jpg" type="image/jpeg" />
|
||||||
|
<link rel="stylesheet" href="css/site.css" />
|
||||||
|
<HeadOutlet />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<Routes />
|
||||||
|
<script src="_framework/blazor.web.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
@inherits LayoutComponentBase
|
||||||
|
@inject AuthenticationStateProvider AuthStateProvider
|
||||||
|
|
||||||
|
<header class="masthead">
|
||||||
|
<div class="masthead-meta">
|
||||||
|
<span>@Krant.Location</span>
|
||||||
|
<span class="masthead-date">@Krant.DutchDate(DateTime.Now)</span>
|
||||||
|
<span>@(_isEditor ? "Redactie: " + _editorName : "NUL EURO")</span>
|
||||||
|
</div>
|
||||||
|
<p class="masthead-title"><a href="/">Proo<span class="long-s">s</span>tenKrant</a></p>
|
||||||
|
<p class="masthead-slogan">„@Krant.Slogan“</p>
|
||||||
|
<nav class="main-nav">
|
||||||
|
<NavLink href="/" Match="NavLinkMatch.All">Voorpagina</NavLink>
|
||||||
|
<NavLink href="/indienen">Krant indienen</NavLink>
|
||||||
|
<NavLink href="/archief">Archief</NavLink>
|
||||||
|
<AuthorizeView>
|
||||||
|
<Authorized>
|
||||||
|
<NavLink href="/dashboard">Dashboard</NavLink>
|
||||||
|
<NavLink href="/artikelen">Artikelen</NavLink>
|
||||||
|
<NavLink href="/seizoenen">Seizoenen</NavLink>
|
||||||
|
<NavLink href="/uitloggen" class="nav-uitloggen">Uitloggen</NavLink>
|
||||||
|
</Authorized>
|
||||||
|
<NotAuthorized>
|
||||||
|
<NavLink href="/login">Inloggen</NavLink>
|
||||||
|
</NotAuthorized>
|
||||||
|
</AuthorizeView>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="sheet">
|
||||||
|
@Body
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="footer-inner">
|
||||||
|
<span>© @DateTime.Now.Year @Krant.AppName — Alle rechten voorbehouden</span>
|
||||||
|
<span class="footer-eu">
|
||||||
|
Made and hosted in
|
||||||
|
<img src="images/european-flag.svg" class="eu-flag" alt="Europese vlag" width="24" height="16" />
|
||||||
|
Europe
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private bool _isEditor;
|
||||||
|
private string? _editorName;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
var state = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||||
|
_isEditor = KrantAuth.IsEditor(state.User);
|
||||||
|
_editorName = KrantAuth.EditorName(state.User);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
@page "/archief"
|
||||||
|
@inject LatexService Latex
|
||||||
|
@inject ArticleService Artikelen
|
||||||
|
@inject SeasonService Seizoenen
|
||||||
|
|
||||||
|
<PageTitle>Archief — ProostenKrant</PageTitle>
|
||||||
|
|
||||||
|
<h1 class="page-heading">Archief</h1>
|
||||||
|
<p class="page-intro">Alle gedrukte edities van de ProostenKrant en de gepubliceerde artikelen per seizoen.</p>
|
||||||
|
|
||||||
|
<h2 class="section-heading">Gedrukte edities</h2>
|
||||||
|
@if (_edities.Count > 0)
|
||||||
|
{
|
||||||
|
<table class="krant-tabel">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Editie</th>
|
||||||
|
<th>Datum</th>
|
||||||
|
<th>Grootte</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var editie in _edities)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@editie.DisplayName</td>
|
||||||
|
<td>@Krant.DutchShortDate(editie.Date)</td>
|
||||||
|
<td>@(editie.SizeBytes / 1024) kB</td>
|
||||||
|
<td><a class="btn" href="/edities/@editie.FileName">Download PDF</a></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<p class="muted">Er zijn nog geen gedrukte edities beschikbaar.</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<h2 class="section-heading">Gepubliceerde artikelen</h2>
|
||||||
|
@foreach (var seizoen in _seizoenenMetArtikelen)
|
||||||
|
{
|
||||||
|
<section class="season-archive">
|
||||||
|
<h3 class="season-heading seizoen-@seizoen.Season?.CssKey">
|
||||||
|
@(seizoen.Season?.Name ?? "Zonder seizoen")
|
||||||
|
@if (seizoen.Season is not null && seizoen.Season.StartDate <= DateTime.Now && seizoen.Season.EndDate >= DateTime.Now)
|
||||||
|
{
|
||||||
|
<span class="tag">huidige editie</span>
|
||||||
|
}
|
||||||
|
</h3>
|
||||||
|
@if (seizoen.Articles.Count == 0)
|
||||||
|
{
|
||||||
|
<p class="muted">Nog geen gepubliceerde artikelen.</p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<ul class="archive-article-list">
|
||||||
|
@foreach (var artikel in seizoen.Articles)
|
||||||
|
{
|
||||||
|
<li>
|
||||||
|
<a href="/artikelen/@artikel.Id" class="archive-article-link">@artikel.Title</a>
|
||||||
|
<span class="byline-inline">— @artikel.AuthorName, @Krant.DutchShortDate(artikel.EventDate)</span>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private List<Edition> _edities = new();
|
||||||
|
private List<(Season? Season, List<Article> Articles)> _seizoenenMetArtikelen = new();
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_edities = Latex.ListEditions();
|
||||||
|
var seasons = await Seizoenen.GetAllAsync();
|
||||||
|
var published = await Artikelen.GetAllAsync("published");
|
||||||
|
_seizoenenMetArtikelen = seasons
|
||||||
|
.Select(s => (s, published.Where(a => a.SeasonId == s.Id).ToList()))
|
||||||
|
.Select(x => (Season: (Season?)x.Item1, Articles: x.Item2))
|
||||||
|
.ToList();
|
||||||
|
var overige = published.Where(a => a.SeasonId is null || seasons.All(s => s.Id != a.SeasonId)).ToList();
|
||||||
|
if (overige.Count > 0)
|
||||||
|
{
|
||||||
|
_seizoenenMetArtikelen.Add((null, overige));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
@page "/artikelen/{Id:int}"
|
||||||
|
@inject ArticleService Artikelen
|
||||||
|
@inject UserService Gebruikers
|
||||||
|
@inject AuthenticationStateProvider AuthStateProvider
|
||||||
|
@inject IWebHostEnvironment Env
|
||||||
|
|
||||||
|
<PageTitle>@(_artikel?.Title ?? "Artikel") — ProostenKrant</PageTitle>
|
||||||
|
|
||||||
|
@if (_artikel is null)
|
||||||
|
{
|
||||||
|
<section class="card">
|
||||||
|
<h1 class="headline">Artikel niet gevonden</h1>
|
||||||
|
<p>Dit artikel bestaat niet (meer). <a href="/artikelen">Naar het overzicht</a>.</p>
|
||||||
|
</section>
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
<article class="article-page">
|
||||||
|
<span class="kicker">@(string.IsNullOrEmpty(_artikel.SeasonName) ? "ProostenKrant" : _artikel.SeasonName)</span>
|
||||||
|
<h1 class="article-headline">@_artikel.Title</h1>
|
||||||
|
<p class="byline">
|
||||||
|
Door @_artikel.AuthorName
|
||||||
|
@if (_artikel.EventDate is not null)
|
||||||
|
{
|
||||||
|
<text>— @Krant.DutchShortDate(_artikel.EventDate)</text>
|
||||||
|
}
|
||||||
|
@if (_isEditor)
|
||||||
|
{
|
||||||
|
<span class="status status-@_artikel.Status">@(_artikel.Status == "published" ? "gepubliceerd" : "klad")</span>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@if (_deelnemers.Count > 0)
|
||||||
|
{
|
||||||
|
<div class="participant-row">
|
||||||
|
@foreach (var deelnemer in _deelnemers)
|
||||||
|
{
|
||||||
|
<span class="participant">
|
||||||
|
<img class="participant-avatar" src="@ProfileWebPath(deelnemer)" alt="@deelnemer.DisplayName" />
|
||||||
|
<span>@deelnemer.DisplayName</span>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(_artikel.WebImage))
|
||||||
|
{
|
||||||
|
<figure class="article-figure">
|
||||||
|
<img src="@_artikel.WebImage" alt="@_artikel.Title" />
|
||||||
|
</figure>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="article-body">
|
||||||
|
@foreach (var alinea in _artikel.Content.Split("\n", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||||||
|
{
|
||||||
|
<p>@alinea</p>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (_isEditor)
|
||||||
|
{
|
||||||
|
<div class="editor-actions">
|
||||||
|
@if (_artikel.Status == "draft")
|
||||||
|
{
|
||||||
|
<EditForm FormName="publiceer" Method="post" Model="PubliceerInput" OnValidSubmit="Publiceer" class="inline-form">
|
||||||
|
<button type="submit" class="btn btn-primary">Publiceren</button>
|
||||||
|
</EditForm>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<EditForm FormName="depubliceer" Method="post" Model="DepubliceerInput" OnValidSubmit="Depubliceer" class="inline-form">
|
||||||
|
<button type="submit" class="btn">Terug naar klad</button>
|
||||||
|
</EditForm>
|
||||||
|
}
|
||||||
|
<a class="btn" href="/indienen/bewerken/@_artikel.Id">Bewerken</a>
|
||||||
|
<EditForm FormName="verwijder" Method="post" Model="VerwijderInput" OnValidSubmit="Verwijder" class="inline-form">
|
||||||
|
<button type="submit" class="btn btn-danger">Verwijderen</button>
|
||||||
|
</EditForm>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</article>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
private Article? _artikel;
|
||||||
|
private List<User> _deelnemers = new();
|
||||||
|
private bool _isEditor;
|
||||||
|
[SupplyParameterFromForm(FormName = "publiceer")]
|
||||||
|
private ActieInput PubliceerInput { get; set; } = new();
|
||||||
|
|
||||||
|
[SupplyParameterFromForm(FormName = "depubliceer")]
|
||||||
|
private ActieInput DepubliceerInput { get; set; } = new();
|
||||||
|
|
||||||
|
[SupplyParameterFromForm(FormName = "verwijder")]
|
||||||
|
private ActieInput VerwijderInput { get; set; } = new();
|
||||||
|
|
||||||
|
private sealed class ActieInput { }
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
PubliceerInput ??= new();
|
||||||
|
DepubliceerInput ??= new();
|
||||||
|
VerwijderInput ??= new();
|
||||||
|
_artikel = await Artikelen.FindAsync(Id);
|
||||||
|
var state = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||||
|
_isEditor = KrantAuth.IsEditor(state.User);
|
||||||
|
if (_artikel is not null && (_artikel.Status == "published" || _isEditor))
|
||||||
|
{
|
||||||
|
_deelnemers = await Gebruikers.GetByIdsAsync(_artikel.ParticipantIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ProfileWebPath(User user) => Gebruikers.GetProfileWebPath(user);
|
||||||
|
|
||||||
|
private async Task Publiceer()
|
||||||
|
{
|
||||||
|
if (_artikel is not null)
|
||||||
|
{
|
||||||
|
await Artikelen.SetStatusAsync(_artikel.Id, "published");
|
||||||
|
Nav.NavigateTo($"/artikelen/{Id}", forceLoad: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Depubliceer()
|
||||||
|
{
|
||||||
|
if (_artikel is not null)
|
||||||
|
{
|
||||||
|
await Artikelen.SetStatusAsync(_artikel.Id, "draft");
|
||||||
|
Nav.NavigateTo($"/artikelen/{Id}", forceLoad: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Verwijder()
|
||||||
|
{
|
||||||
|
if (_artikel is not null)
|
||||||
|
{
|
||||||
|
await Artikelen.DeleteAsync(_artikel.Id);
|
||||||
|
Nav.NavigateTo("/artikelen", forceLoad: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
@page "/artikelen/nieuw"
|
||||||
|
@attribute [Authorize(Policy = "Editor")]
|
||||||
|
@using System.ComponentModel.DataAnnotations
|
||||||
|
@inject ArticleService Artikelen
|
||||||
|
@inject SeasonService Seizoenen
|
||||||
|
@inject UserService Gebruikers
|
||||||
|
@inject UploadService Uploads
|
||||||
|
|
||||||
|
<PageTitle>Nieuw artikel — ProostenKrant</PageTitle>
|
||||||
|
|
||||||
|
<section class="card">
|
||||||
|
<h1 class="headline">Nieuw artikel</h1>
|
||||||
|
<p class="muted">Artikel voor de redactie aanmaken en eventueel direct publiceren.</p>
|
||||||
|
|
||||||
|
@if (_fout is not null)
|
||||||
|
{
|
||||||
|
<p class="alert alert-error">@_fout</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<EditForm FormName="artikel-nieuw" Method="post" Model="Input" OnValidSubmit="Opslaan"
|
||||||
|
class="krant-form" enctype="multipart/form-data">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="title">Titel</label>
|
||||||
|
<InputText @bind-Value="Input.Title" id="title" class="form-control" placeholder="De kop boven het artikel" />
|
||||||
|
<ValidationMessage For="() => Input.Title" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="content">Tekst</label>
|
||||||
|
<InputTextArea @bind-Value="Input.Content" id="content" rows="10" class="form-control" placeholder="Schrijf hier de inhoud van je artikel…" />
|
||||||
|
<ValidationMessage For="() => Input.Content" />
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="writer">Schrijver</label>
|
||||||
|
<InputSelect @bind-Value="Input.WriterId" id="writer" class="form-control">
|
||||||
|
<option value="0">— Kies schrijver —</option>
|
||||||
|
@foreach (var gebruiker in _gebruikers)
|
||||||
|
{
|
||||||
|
<option value="@gebruiker.Id">@gebruiker.DisplayName</option>
|
||||||
|
}
|
||||||
|
</InputSelect>
|
||||||
|
<ValidationMessage For="() => Input.WriterId" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="eventdate">Datum van de gebeurtenis</label>
|
||||||
|
<InputDate @bind-Value="Input.EventDate" id="eventdate" class="form-control" />
|
||||||
|
<ValidationMessage For="() => Input.EventDate" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="season">Seizoen</label>
|
||||||
|
<InputSelect @bind-Value="Input.SeasonId" id="season" class="form-control">
|
||||||
|
<option value="0">— Kies seizoen —</option>
|
||||||
|
@foreach (var seizoen in _seizoenen)
|
||||||
|
{
|
||||||
|
<option value="@seizoen.Id">@seizoen.Name</option>
|
||||||
|
}
|
||||||
|
</InputSelect>
|
||||||
|
<ValidationMessage For="() => Input.SeasonId" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="status">Status</label>
|
||||||
|
<InputSelect @bind-Value="Input.Status" id="status" class="form-control">
|
||||||
|
<option value="draft">Klad</option>
|
||||||
|
<option value="published">Gepubliceerd</option>
|
||||||
|
</InputSelect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Deelnemers</label>
|
||||||
|
<div class="checkbox-group">
|
||||||
|
@foreach (var gebruiker in _gebruikers)
|
||||||
|
{
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" name="Participants" value="@gebruiker.Id"
|
||||||
|
checked="@(_geselecteerd.Contains(gebruiker.Id))" />
|
||||||
|
<span>@gebruiker.DisplayName</span>
|
||||||
|
</label>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (_participantsFout is not null)
|
||||||
|
{
|
||||||
|
<span class="error-message">@_participantsFout</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="image">Afbeelding (optioneel)</label>
|
||||||
|
<input type="file" name="Afbeelding" id="image" class="form-control" accept="image/*" />
|
||||||
|
<span class="help-text">JPG, PNG, GIF of WEBP, maximaal 25 MB.</span>
|
||||||
|
@if (_afbeeldingFout is not null)
|
||||||
|
{
|
||||||
|
<span class="error-message">@_afbeeldingFout</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="submit" class="btn btn-primary">Artikel opslaan</button>
|
||||||
|
<a class="btn" href="/artikelen">Annuleren</a>
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[CascadingParameter]
|
||||||
|
private HttpContext HttpContext { get; set; } = default!;
|
||||||
|
|
||||||
|
[SupplyParameterFromForm(FormName = "artikel-nieuw")]
|
||||||
|
private ArtikelInput Input { get; set; } = new();
|
||||||
|
|
||||||
|
private List<User> _gebruikers = new();
|
||||||
|
private List<Season> _seizoenen = new();
|
||||||
|
private List<int> _geselecteerd = new();
|
||||||
|
private string? _fout;
|
||||||
|
private string? _participantsFout;
|
||||||
|
private string? _afbeeldingFout;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
Input ??= new();
|
||||||
|
_gebruikers = await Gebruikers.GetAllAsync();
|
||||||
|
_seizoenen = await Seizoenen.GetAllAsync();
|
||||||
|
var huidig = await Seizoenen.GetCurrentAsync();
|
||||||
|
Input.SeasonId ??= huidig?.Id ?? (_seizoenen.FirstOrDefault()?.Id ?? 0);
|
||||||
|
Input.EventDate ??= DateTime.Today;
|
||||||
|
Input.Status ??= "published";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Opslaan()
|
||||||
|
{
|
||||||
|
Input ??= new();
|
||||||
|
var form = HttpContext.Request.Form;
|
||||||
|
_geselecteerd = form["Participants"].SelectMany(v => (v ?? "").Split(','))
|
||||||
|
.Select(s => int.TryParse(s, out var id) ? id : 0).Where(id => id > 0).ToList();
|
||||||
|
if (Input.WriterId <= 0)
|
||||||
|
{
|
||||||
|
_fout = "Kies een schrijver.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_geselecteerd.Contains(Input.WriterId))
|
||||||
|
{
|
||||||
|
_geselecteerd.Insert(0, Input.WriterId);
|
||||||
|
}
|
||||||
|
if (_geselecteerd.Count == 0)
|
||||||
|
{
|
||||||
|
_participantsFout = "Selecteer minstens één deelnemer.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (webPad, fout) = await Uploads.SaveArticleImageAsync(HttpContext.Request.Form.Files["Afbeelding"]);
|
||||||
|
if (fout is not null)
|
||||||
|
{
|
||||||
|
_afbeeldingFout = fout;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var artikel = new Article
|
||||||
|
{
|
||||||
|
Title = Input.Title,
|
||||||
|
Content = Input.Content,
|
||||||
|
AuthorId = Input.WriterId,
|
||||||
|
Participants = string.Join(",", _geselecteerd),
|
||||||
|
EventDate = Input.EventDate,
|
||||||
|
SeasonId = Input.SeasonId,
|
||||||
|
ArticleImage = webPad,
|
||||||
|
Status = Input.Status == "published" ? "published" : "draft",
|
||||||
|
};
|
||||||
|
await Artikelen.CreateAsync(artikel);
|
||||||
|
Nav.NavigateTo("/artikelen?melding=" + Uri.EscapeDataString("Artikel succesvol opgeslagen."), forceLoad: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class ArtikelInput
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "Geef het artikel een titel (minstens 5 tekens).")]
|
||||||
|
[MinLength(5, ErrorMessage = "De titel moet minstens 5 tekens bevatten.")]
|
||||||
|
[MaxLength(255)]
|
||||||
|
public string Title { get; set; } = "";
|
||||||
|
|
||||||
|
[Required(ErrorMessage = "Schrijf de tekst van het artikel (minstens 10 tekens).")]
|
||||||
|
[MinLength(10, ErrorMessage = "De tekst moet minstens 10 tekens bevatten.")]
|
||||||
|
public string Content { get; set; } = "";
|
||||||
|
|
||||||
|
public int WriterId { get; set; }
|
||||||
|
|
||||||
|
public DateTime? EventDate { get; set; }
|
||||||
|
|
||||||
|
public int? SeasonId { get; set; }
|
||||||
|
|
||||||
|
public string? Status { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
@page "/artikelen"
|
||||||
|
@attribute [Authorize(Policy = "Editor")]
|
||||||
|
@inject ArticleService ArtikelSvc
|
||||||
|
|
||||||
|
<PageTitle>Artikelen — ProostenKrant</PageTitle>
|
||||||
|
|
||||||
|
<h1 class="page-heading">Artikelen</h1>
|
||||||
|
|
||||||
|
<nav class="filter-bar">
|
||||||
|
<a href="/artikelen" class="btn @(SelectedStatus is null ? "btn-active" : "")">Alle</a>
|
||||||
|
<a href="/artikelen?status=draft" class="btn @(_status == "draft" ? "btn-active" : "")">Klad</a>
|
||||||
|
<a href="/artikelen?status=published" class="btn @(_status == "published" ? "btn-active" : "")">Gepubliceerd</a>
|
||||||
|
<a href="/artikelen/nieuw" class="btn btn-primary">Nieuw artikel</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
@if (_artikelen.Count == 0)
|
||||||
|
{
|
||||||
|
<p class="muted">Geen artikelen gevonden.</p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<table class="krant-tabel">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Titel</th>
|
||||||
|
<th>Schrijver</th>
|
||||||
|
<th>Gebeurtenis</th>
|
||||||
|
<th>Seizoen</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var artikel in _artikelen)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td><a href="/artikelen/@artikel.Id">@artikel.Title</a></td>
|
||||||
|
<td>@artikel.AuthorName</td>
|
||||||
|
<td>@Krant.DutchShortDate(artikel.EventDate)</td>
|
||||||
|
<td>@(artikel.SeasonName ?? "—")</td>
|
||||||
|
<td><span class="status status-@artikel.Status">@(artikel.Status == "published" ? "gepubliceerd" : "klad")</span></td>
|
||||||
|
<td><a class="btn" href="/artikelen/@artikel.Id">Openen</a></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[SupplyParameterFromQuery(Name = "status")]
|
||||||
|
public string? Status { get; set; }
|
||||||
|
|
||||||
|
private string? _status;
|
||||||
|
private List<Article> _artikelen = new();
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
_status = Status;
|
||||||
|
_artikelen = await ArtikelSvc.GetAllAsync(string.IsNullOrEmpty(Status) ? null : Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? SelectedStatus => _status;
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
@page "/indienen/bewerken/{Id:int}"
|
||||||
|
@using System.ComponentModel.DataAnnotations
|
||||||
|
@inject ArticleService Artikelen
|
||||||
|
@inject UserService Gebruikers
|
||||||
|
@inject AuthenticationStateProvider AuthStateProvider
|
||||||
|
|
||||||
|
<PageTitle>Verifieer schrijver — ProostenKrant</PageTitle>
|
||||||
|
|
||||||
|
@if (_artikel is null)
|
||||||
|
{
|
||||||
|
<section class="card">
|
||||||
|
<h1 class="headline">Artikel niet gevonden</h1>
|
||||||
|
<p><a href="/indienen/bewerken/lijst">Terug naar de lijst</a>.</p>
|
||||||
|
</section>
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
<section class="card auth-card">
|
||||||
|
<span class="kicker">Beveiliging</span>
|
||||||
|
<h1 class="headline">Wie schreef dit artikel?</h1>
|
||||||
|
<p class="muted">
|
||||||
|
Om „@_artikel.Title“ te bewerken, bevestig je eerst dat jij de schrijver bent.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
@if (_fout is not null)
|
||||||
|
{
|
||||||
|
<p class="alert alert-error">@_fout</p>
|
||||||
|
}
|
||||||
|
|
||||||
|
<EditForm FormName="verifieer-schrijver" Method="post" Model="Input" OnValidSubmit="Verifieer" class="krant-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="writer">Schrijver</label>
|
||||||
|
<InputSelect @bind-Value="Input.WriterId" id="writer" class="form-control">
|
||||||
|
<option value="0">— Kies schrijver —</option>
|
||||||
|
@foreach (var gebruiker in _gebruikers)
|
||||||
|
{
|
||||||
|
<option value="@gebruiker.Id">@gebruiker.DisplayName</option>
|
||||||
|
}
|
||||||
|
</InputSelect>
|
||||||
|
<ValidationMessage For="() => Input.WriterId" />
|
||||||
|
</div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="submit" class="btn btn-primary">Doorgaan naar bewerken</button>
|
||||||
|
<a class="btn" href="/indienen/bewerken/lijst">Annuleren</a>
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[CascadingParameter]
|
||||||
|
private HttpContext HttpContext { get; set; } = default!;
|
||||||
|
|
||||||
|
[SupplyParameterFromForm(FormName = "verifieer-schrijver")]
|
||||||
|
private VerificatieInput Input { get; set; } = new();
|
||||||
|
|
||||||
|
private Article? _artikel;
|
||||||
|
private List<User> _gebruikers = new();
|
||||||
|
private string? _fout;
|
||||||
|
|
||||||
|
private sealed class VerificatieInput
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "Kies de schrijver.")]
|
||||||
|
public int WriterId { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
Input ??= new();
|
||||||
|
var state = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||||
|
if (!KrantAuth.HasValidPassphrase(state.User) && !KrantAuth.IsEditor(state.User))
|
||||||
|
{
|
||||||
|
Nav.NavigateTo("/indienen/bewerken", forceLoad: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_artikel = await Artikelen.FindAsync(Id);
|
||||||
|
_gebruikers = await Gebruikers.GetAllAsync();
|
||||||
|
|
||||||
|
// Een ingelogd redactielid dat zelf de schrijver is, hoeft zich niet
|
||||||
|
// te verifiëren: direct door naar het formulier, met behoud van de
|
||||||
|
// redactiesessie. Artikelen van een andere schrijver volgen de
|
||||||
|
// gewone controle hieronder.
|
||||||
|
if (KrantAuth.IsEditor(state.User) && KrantAuth.Uid(state.User) is int uid &&
|
||||||
|
_artikel is not null && uid == _artikel.AuthorId)
|
||||||
|
{
|
||||||
|
var gebruiker = await Gebruikers.FindAsync(uid);
|
||||||
|
if (gebruiker is not null)
|
||||||
|
{
|
||||||
|
await HttpContext.SignInAsync(KrantAuth.Scheme,
|
||||||
|
KrantAuth.EditorPrincipal(gebruiker, editArticleId: _artikel.Id));
|
||||||
|
Nav.NavigateTo("/indienen/formulier", forceLoad: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Verifieer()
|
||||||
|
{
|
||||||
|
if (_artikel is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Input.WriterId != _artikel.AuthorId)
|
||||||
|
{
|
||||||
|
_fout = "Alleen de schrijver van het artikel kan het bewerken.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await HttpContext.SignInAsync(KrantAuth.Scheme,
|
||||||
|
KrantAuth.PassphrasePrincipal(editArticleId: _artikel.Id));
|
||||||
|
Nav.NavigateTo("/indienen/formulier", forceLoad: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
@page "/indienen/bewerken/lijst"
|
||||||
|
@inject ArticleService Artikelen
|
||||||
|
@inject AuthenticationStateProvider AuthStateProvider
|
||||||
|
|
||||||
|
<PageTitle>Kladartikelen — ProostenKrant</PageTitle>
|
||||||
|
|
||||||
|
<h1 class="page-heading">Kladartikelen bewerken</h1>
|
||||||
|
<p class="page-intro">Kies het artikel dat je wilt aanpassen. Alleen de schrijver van het artikel kan het bewerken.</p>
|
||||||
|
|
||||||
|
@if (_klad.Count == 0)
|
||||||
|
{
|
||||||
|
<p class="muted">Er zijn momenteel geen kladartikelen.</p>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<table class="krant-tabel">
|
||||||
|
<thead>
|
||||||
|
<tr><th>Titel</th><th>Schrijver</th><th>Gebeurtenis</th><th></th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var artikel in _klad)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@artikel.Title</td>
|
||||||
|
<td>@artikel.AuthorName</td>
|
||||||
|
<td>@Krant.DutchShortDate(artikel.EventDate)</td>
|
||||||
|
<td><a class="btn" href="/indienen/bewerken/@artikel.Id">Bewerken</a></td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private List<Article> _klad = new();
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
var state = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||||
|
if (!KrantAuth.HasValidPassphrase(state.User) && !KrantAuth.IsEditor(state.User))
|
||||||
|
{
|
||||||
|
Nav.NavigateTo("/indienen/bewerken", forceLoad: true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_klad = await Artikelen.GetAllAsync("draft");
|
||||||
|
}
|
||||||
|
}
|
||||||