From 91f5318d9b6e3d57490511aa12816b54fc31481e Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 27 Aug 2026 11:37:44 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E2=80=9ENeuen=20Datensatz=20anlegen"=20?= =?UTF-8?q?startet=20bei=200=20%,=20nicht=20bei=207=20%=20(v2.41.1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zwei Ursachen, beide dieselbe Sorte Fehler: Der Wizard rechnete sich an, was er selbst vorgibt. WordPress legt beim Öffnen des Formulars einen Auto-Entwurf an und feuert dabei save_post. set_modified_date() stempelte daraufhin das Änderungsdatum, obwohl niemand etwas gespeichert hatte — 5 Punkte. Und Carbon Fields liefert für ungespeicherte Felder ihren Vorgabewert zurück: Die Zugriffsrechte stehen auf „öffentlich", das zählte als Angabe plus Vokabulartreffer — 10 + 5 Punkte. Zusammen 20 von 295 bewertbaren Punkten, also die gemeldeten 7 %. Der Bericht wertet auf einem Auto-Entwurf jetzt nichts als erfüllt, und set_modified_date() überspringt ihn (auf einen Auto-Entwurf gehört kein dct:modified — es datierte den Datensatz auf das Aufrufen des Formulars). Der Nenner bleibt stehen: Die Metriken sind prüfbar, sie sind nur noch nicht erfüllt. Wären sie „nicht bewertbar", ergäbe 0 von 0 Punkten wieder eine Zahl, die niemand erwartet. Der Erklärtext im Bericht behauptete bisher, ein leerer Datensatz stehe deshalb nie bei 0 % — das stimmt jetzt nicht mehr und unterschlug außerdem die 15 Punkte aus den vorbelegten Feldern. Neu formuliert. Tests: zwei Unit-Tests (derselbe Meta-Wert zählt im Entwurf, nicht im Auto-Entwurf; calculate() ergibt 0 bei stehendem Nenner) und ein E2E-Test auf die 0 % im frisch geöffneten Formular. https://claude.ai/code/session_01JB1xUQM892bVZ4Yv3MZjvq --- CHANGELOG.md | 18 +++++ CLAUDE.md | 2 +- README.md | 2 +- includes/class-fields.php | 9 +++ includes/class-quality.php | 24 +++++-- languages/open-data-wizard-en_US.mo | Bin 67863 -> 68075 bytes languages/open-data-wizard-en_US.po | 4 +- languages/open-data-wizard.pot | 2 +- open-data-wizard.php | 4 +- package.json | 2 +- tests/e2e/admin-workflow.spec.js | 11 +++ tests/test-quality.php | 108 ++++++++++++++++++++++++++++ 12 files changed, 172 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f59ce60..972aaebf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,24 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/spec/v2.0.0.ht --- +## [2.41.1] — 2026-08-27 + +Ein Nachtrag zum Usability-Test: der Startwert der Qualitätsprüfung. + +### 🐛 Fixed +- **„Neuen Datensatz anlegen" startete bei 7 %, nicht bei 0 %.** Zwei Ursachen, beide + dieselbe Sorte Fehler — der Wizard rechnete sich an, was er selbst vorgibt. WordPress legt + beim Öffnen des Formulars einen Auto-Entwurf an und feuert dabei `save_post`: Das + Änderungsdatum wurde gestempelt (5 Punkte), obwohl niemand etwas gespeichert hatte. Und + Carbon Fields liefert für ungespeicherte Felder ihren Vorgabewert zurück — die Zugriffsrechte + stehen auf „öffentlich", das zählte als Angabe plus Vokabulartreffer (10 + 5 Punkte). + Zusammen 20 von 295 bewertbaren Punkten. Der Bericht wertet auf einem Auto-Entwurf jetzt + nichts als erfüllt, und `set_modified_date()` überspringt ihn. Der Nenner bleibt dabei + stehen: Die Metriken sind prüfbar, sie sind nur noch nicht erfüllt — 0 von 0 Punkten ergäbe + wieder eine überraschende Zahl. Zwei Unit-Tests und ein E2E-Test halten die 0 fest. + +--- + ## [2.41.0] — 2026-08-27 Fünf Punkte aus dem Usability-Test. Hilfetexte haben jetzt je einen Ort, die Definition zeigt diff --git a/CLAUDE.md b/CLAUDE.md index b878b8f4..62f5cb3b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -765,7 +765,7 @@ Update **both** locations: - MINOR: New features (backward-compatible) - PATCH: Bug fixes only -Current: **v2.39.1** +Current: **v2.41.1** --- diff --git a/README.md b/README.md index d537bbaa..ce4e0584 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

Lizenz - Version + Version DCAT-AP 3.0 PHP >= 8.1 WordPress 6.4+ diff --git a/includes/class-fields.php b/includes/class-fields.php index 5fcd4e2f..cf25b8b3 100644 --- a/includes/class-fields.php +++ b/includes/class-fields.php @@ -695,6 +695,15 @@ public static function set_modified_date( int $post_id, \WP_Post $post ): void { return; } + // Auto-Entwürfe überspringen: WordPress legt sie beim Öffnen von „Neuer + // Datensatz" an und feuert dabei save_post. Ein dct:modified zu schreiben, + // bevor überhaupt jemand etwas eingetragen hat, datiert den Datensatz auf + // das Aufrufen des Formulars — und ließ die Qualitätsprüfung mit Punkten + // starten, die niemand verdient hatte. + if ( 'auto-draft' === $post->post_status ) { + return; + } + remove_action( 'save_post', array( self::class, 'set_modified_date' ), 20 ); update_post_meta( $post_id, '_odw_modified', current_time( 'Y-m-d' ) ); add_action( 'save_post', array( self::class, 'set_modified_date' ), 20, 2 ); diff --git a/includes/class-quality.php b/includes/class-quality.php index 01f04a1a..6c0d94f8 100644 --- a/includes/class-quality.php +++ b/includes/class-quality.php @@ -202,16 +202,28 @@ public static function calculate( int $post_id ): array { private static function evaluate_metric( array $metric, \WP_Post $post ): ?bool { $type = (string) ( $metric['type'] ?? '' ); + // Ein Auto-Entwurf ist nichts, was jemand gespeichert hat: WordPress legt + // ihn beim Öffnen von „Neuer Datensatz" selbst an. Carbon Fields liefert + // für ungespeicherte Felder ihren Vorgabewert zurück — bei den + // Zugriffsrechten etwa „öffentlich". Ohne diese Abfrage zählte der Bericht + // also, was das Formular vorschlägt, statt was jemand eingetragen hat, und + // ein frisch geöffnetes Formular stünde nicht bei 0 %. + // + // Nicht erfüllt, nicht „nicht bewertbar": Die Metrik ist prüfbar, sie ist + // nur (noch) nicht erfüllt. Sonst schrumpfte der Nenner und 0 von 0 + // Punkten ergäbe wieder eine Zahl, die niemand erwartet. + $ungespeichert = 'auto-draft' === $post->post_status; + if ( 'present' === $type ) { - return self::check_metric( (string) $metric['check'], $post ); + return $ungespeichert ? false : self::check_metric( (string) $metric['check'], $post ); } if ( 'vocab' === $type ) { - return self::check_vocab_metric( (string) $metric['check'], $post ); + return $ungespeichert ? false : self::check_vocab_metric( (string) $metric['check'], $post ); } if ( 'reachable' === $type && self::url_checks_enabled() ) { - return self::check_reachable_metric( (string) $metric['check'], $post ); + return $ungespeichert ? false : self::check_reachable_metric( (string) $metric['check'], $post ); } // SHACL (und Erreichbarkeit bei deaktivierter Einstellung) folgt in Phase 3+. @@ -779,8 +791,8 @@ public static function render_meta_box( \WP_Post $post ): void { fHa08d~roZOv1CM{(M0rEUONU zrl<$woH!Y^2Rh>z9D(X!opXOXHX!~Sd*BIdh7CtnubP#L1;kIG2D*TC{4DDRsP7! zj#<-OOeS81nn@*Y#q+3Bu`JhAWF0EutvDQyU@vUuG8LVGHt`lzC61v^kMBns>iB2J zzffz|Adim{c01fIX}h5++8;CVF&v3oocsQK9GHO5 z3ZbEno1-%8k6N2d)Nam44uUlegRlyF;%Th#Be_C4#Y=1;CGbX(IsX-?({mTKXKEIk z{(@0^AR5zj{?lm0aiI`(OkTubJb>EO$I%~uLcRD4HbMUqv$ip)ilpFh9E^|RGE`-6 zU}p^ABa?wWQG3OQg^X`i(5R0c%h)*B2bEBlDVDVs$D@v?zuWu}3U};^aC_%bT7Zcms2 z9!IVHBGiXzBL?6ORKj~v6Z-~ReXQ%`&@vB#tC3M|h=9yA8MpYyo6R{6Yz#`NxKZIJ6lc-XM zJa6ocJ&8RSfLpK$eu8m~Zyl$RhJWY+rpz}3W}?>KgDr6$s)K)_mTD_D$9?z!UUK3V zFPJ6mhw3j2_52KE-&!xAPC@Mj)IX3$CmKQ66IJ4&j+0Q8n1R)s2os6VU>mH%j#UC_ zsM9bC_5MGw8P0a%w^2*`F~;KmU^3oY$oebu_(dku9BfDYJgS7-u`6Cdy%6`J`8hul zHG{dRwfqp{@jB}L$i?RL48#t^WvC@yjjH5N7>)IoQ2$Xh5|;3tMjtA3_etQ4>Kb4)GY&%qL+@ zT!1YY-&*WEu*LCH)C>+_6Fh>Ecp0P7?@cq47}TchfV%!T*29_D9iK-fz8|&3C$KTz zLM0yj7RfWdWz(pSZBYYu$8a3wScHv;XJa2+hE4rAMyLdSFE>lk;%zhI!I;VQ$v7T& zVF5<1GRJccD$y0_Qz_TdsEa#LOR*1!;!#v7o4sQykc7>M`=S!c#t58-dTtSF$yQ(| z+>dE^AN3_|zuHu`3u@D5tfv0@qPe&*5Q|Y?s%@x54xkb_hDtQ{U6Vj6#t=V*daeXj z@~2Rh+m73@64@r!yfx+@C|bN{j_qzt;`)vEsDC<*n6<{ys4v?R)UMu%D%BM%!rQ3L zl=Hr+STSmEOh?Ud393TNQ2lH}C3qGa;x$LVbtVq?(J13aB96u7n2NVB4qJ0Z)X`8> zJPkF#3hazK@ErbzO7z&j%>6T{&Gr{I#fS~&_@-itA3s%5OX!=kkxpp5fqF3W1M`V3 zL6!I&%)-5>(nkE-n2Z6$eXte|L2cdPSN+nt=My zOvX@L;$$Yt-gFgF2?Q_L#U0MiY-fElDxjI1AP9Dj$t-8i!B=oJDn1bFV2;B0fvp0oB1r zsLU%c6MsV`((iMY0Y{-qI}SCWT-1!+sHK~YO6VmlLEi^7)N!*foNqL0Pjta}9EvKP z$GJZXYZ1?L;zih;cq#V7k5FrVAC*YJK69Kyu@iA~)FvH`JsIEf(CEvBEvSKR<1MVY z-z0JuuM!6xFf+P~`tZ~~XcDfEF~s4hnWmxkR6ob*SetkQ#^Y9u#2+w0=l>=R%^>YTyf~CAx}j@Gtbogs(`L z@vXKrlvxi{XhIcUuM^~Eno<%K5IVzD| z*a*Kw^>Z47@YhP}uMY2XLAx^Sh#4>i6Nv|42Q0${xEjY}yDIbN_(Dt|{u)(*+o;X# zchr==5vt!Pd;rri4ZTjh?kM#S;KD^NXhzpC82!F6iG*N%;&=?j&KQORQHhVm23U$J z^=yp5m8kn$u`yPlDsT>m;vEdZzP@87flSneaj3QahjV=qD)U#dB_72Y_&aKklpZ&K z0lkmO#OF~1ho0cqF>H+u@I%xdIOxPxIE2`Do<<6dlyA*3oPe>!t59pS7geE3499O# zd*T;tjsD-6U7dugz$ny@=`z%2e$lzV9yPHO*dFg7r@&_=d~Z5zi#kUAQO9Qozbq}|TO(-b zg@u@k<){aaVowY^!*9Ac0+ra?$Rw5gk<8T-D z!t?0UF>86w+!&4%iQSIJ&_$efo}c-+9M9r4+<=uA%s2n3i)P?Gn9lX17=v{$nNlZW zEO8cUNxi5_`z}#`U6{uOmFi`T#I>l?up4#0kDz9L7d7KrKk=`g7>l)WKDNOn7=Sxb zd*Tahh(~cEUPM)Rz|ZFT^q+kuvxQvH9@v0difyP<@g=H*JE#HbT{eFQbjAeY)u@sm z#y~ucJ@680ptvjM{wP#M@=^UghnnbepVQca>fn$QA48k?suKrYH8YFD0n8i9WWP%q3&-+eJ4IfEnx*F<0;hcuk#=COKUvp`9m0^^It{d2`*g6u9$zrd}3Fj z;;5VE9}*uyRj3M8@;bLn$2Qg@?un|%5DdV{*b3b^5?@Cx*%eH}TiBNIE&F%#;)B?k zxCB+wHK>_x!sd7gmB25k8MV4??2BQ<*=VB&HPBL2VsD{7#p|&n9zZSmJ@o0p@H?iX zSXA5ti*O*e#t%_5`T^_Xc?`!Js8R>sHNU(@p=R6@m0&I=;%w9=+<=kz6*k86cd5T- z_9qt_Vay-q6WbAO;?bxt)^zNOZ=yQ>4pZ=V$GCgu7ne-bnlHc(xE&MlD)zue_subW z7?s%5_o=@!Tg!!ZcmUhuZES+^e;Rw^SmJTWfv~n=1itf^S(+WFfq%f682GnYlDVjo zFGC&MubepTzoxPUJ{qdPV$`PCjDztsD&u&|uX+t#7)v|_)A1$Lu{wlW`!GMh>I$|* zO&}drfkNzxD=`MY#ZLG;MxigMhF|qxKm$?7aHYo7VKJUWx}$ z1B^vY;1yJ&yHU@dK@EH#wUkZ$&HKHO&FQly($Hooa&F8;ZL;?;54YiRjIQNZeJVc2 zVB*iQ8CIb>{?)nOu(n_IrfrVe{e!R}W}!B50e**N7^w3eD$BYv8-6 zC0dJtSmF2$R-X#gu0HR0A2p$dfo9-jY)hPhO1K0=aUl+zK;umsTEmNI;~muIYRvyH zp$FTc63f6K9EKWjEb16eK^?<+PP`nu5pSxqwQ0iG#=icAC3cBxs-0bu?<#WTdtGk3 zU$)m(;>q?tX?wh`08Y)sh;fOVlJDfO6&oyqFin!+qs1EnqlX-U1Xn2=iY3O%WHc{#+6skBX(g4 OAJWf;q delta 9332 zcmXZg33N@@9>?(=A`w$WL?VbANhBf&qGA@*oElT4x_kZtw?v-1!J?G8x+&J0Oz0Meur=c-L za3gNQU04!_CK$5@r(qh#H8N%oF2_RHx3MuZFasB%X<|%%Sd3g^R$;P-F*orraiga8 zIlj$|=|w!YnQ@yW8t3VV!`SA=RK!%gjx&*eW>gDf;&CsQz!w;b#ah}#;?S2k0VA*- zR>o}9#8#ma-h#}~<*Pw1Vhmn}KwJ|QNhn*Jf>MkyNGP&b~&Zuks~V#oK4@y7mG7}HS?9*g;L z4l2>bsON0L(Rc)vSo1da{;pVrxDQsup{VD%7t`>e@il6oEl#`xwFiE}WW0=eK<&2n z`W9G>I0@@ve=LJ5k<~GKaR3%-X9pUNKJ0}_sD!7tXURNR|K&7_(y@t+pak}!7al@A z@HlEFe`96z=wM7Gj74476II$&R0TdnUAGFgB&V?oK0;L{q9f15WthzPW>_aHp?I^j%!hyaSs;20+b~f15j%mi+Qmd24F9ze>kd=%die^#~Sz;%Q3!* z>0(RN1~s!DsESNO4YUX~&}!7?*^8R#&!{#03+v)j)DqR`YL})7GKT4lHE|>Az8k0n zJ-e~~Y6R2JfU&6Km4K<3j7n%Hy6`rZ!@}L|9*9NVUmLX-+M*Kbi=}WV>a^q<0d#OK{K^q}BA#*D#+7?0m$U3`QwSgo&}NiWPH&O%its-LY$ zZB)WdFcEuW16+@)=v{PSW7bub=!ZHz?qM`kqEU_?qt@(mjKW;hj2~hc`t`SKSp&7} z+hSQ9gF0plQJXdwRnaq;gnwf@Z2T|#`CMcIZnKPr9=sWq*%{Q@Tt)5Xr^v=O-n>l; zVG7pAbUf%`%sSkR*=!&s5S?bve^*o@AE5Tk9Mtocp!UEzjAMNBBaLu6o?}fcJJ=@D z8MUiZ(Gy3aZXAataV~0YH=uU+E=-HodQ+AeW?xWsuoCfP$IZx?<`QynOq)!; z2XH;Q)$q&WXkixa#@@q?X^NpE>?Rw4ZHY&t63ayma1XWiA=&oDR3E*GTcQ$fkD6Ft zERVymFn)#=aCJ8IFGS-w9V+2P9EH!&3o}RB-9H+&hO4m{Zo(kkhpd{pfI2NLxmkN> zDr!$H#QeAmbu15HWxR;GuJCB;A5SB6H0z69Q5T#<4U~s7sRsmLLkz(h*b8-P=AxG1 zD-6UVsEJ&|5@^QS$^@eN!_bBG9ecTHXn+a$5q^N0X^C<6__D+1GW1zP>F0n zmHZk8;T_b#UgPb6Ww0i3JXXaF?0_p!OXq$6eEU>DTx z&O*(wAxQ589e74RhXz*neU-fgN~l0m3aFLOMB^@%cCy70k5Lg-tz;yBths+To<)eO)wNYVjawO;vCeHo<=?I8tVE2AKLGjQm9Qn58e4` z?5Ck)cobFQ^NtTtl_)UH*+f`@I0K_`0V;tXQK#WL>i(Bl2L0L5imPHdY>t}PzcB`< zeMJ40`BplV=|il7C8ygGw!m7%V^BA2!RmMgHG>i#+qHccBZ()V?q7pZcowUn#|*o~ zaTrEC5=-Nv8PvZMjcs(?$D*Iu%=6B)Ka)dI87CqinZc-;zD7ORf0q62*c3I9Z?O{I zM=Xe{ht-7If6cM4XIzW1T>~IBsBN%sbaEMK!EPJP1{} zH5i9SP^HfEsq@PS3lXQF?oUT0wivaP?)@~hR`*bAS!AAlL&ahOaXXB_*{ITOb3VU- z<%wUT9vm^>4%`8?r?OF%n}9A{hW@w*N8oAvO6R}zXEwtpScDHe7udD&M`aj`zStFA zI0*f48WzLF&gUEP8S!o`jN2F5>kpt0@lDjsA7EZAy+{eO{w^B2ps`~jY6hLLB=*KY z9F3)M7HTFNP@D1xr~e)nBrd$zei@ZSCEf``us;Uj6jb6%un^;$O*Ay~ou~niU;tis ze1*k{{g>GPR!3p52geANz<-w7rN}`S@voSKFR(kdS!Vw$wiZhe2Q0UV#-LlJjHgim zTcS2gM{I?CP^Dats=)VH22Y_9x{rFG_X>Mm2x`e4bx_*O+Np*vt5XPQPd>cXp79p9izTz$R$!O$MHDOWr0KyTuc zm=AwLZQjcmg)gu&hHtPFNWu!lsL?yBqeQ_K5;X$YWH>^bb1br|fhtC<`RH32LC7?3zhI(Ko7C|>w z$B$7Z-HR&yKd8z*bM*SgCg6|7=&yoGq!H@6_Kv+!uK!(N2)Se$qqhT<$#1-?N| za1Uxv9N$6xwI)yL&{~$?X)lb!`osyS-8=!cN4`XDp2Mh_Uw3?l+PnpK*<)K36?ekY zI2N@epP>uaqMm!iO+%aMF=~K9yX}otP$lY!ldvc10as9&Kf)v|y~ie!fu6(@QTI(n zP3RNUjF+I6ZapfY9hi>pUuo#UE%w?sSako$ic4@27Cm5plC8t8 z#4k|ScRFYj9*GIWQ?LO3g!S+QYT%-W>=FfHG;sygQguLI#y4GPMB+eHX=Y+PuEVPM zCu+c;TsvTGtVrApRoYA}fRj-(n}I&K8ueb;gxV9isPBwZ&gTUWQ-5uW02(@G;aC&L zp%U4Gdf;)?KsQmF^*L&3iXO3lPzcA0#FJ3>Z9pwaE-I0~P{;8h>N!3?+ld7KO#Su1 zNIJAD>!AkhiWP7eR>MU&4Ub}X>~_@tcYG^G5kEs!pxiOLnJb}6-w^fOW>^RNVH_@V z;?u{dzc(Fz$L)+tVqxM+s6^^u5lq6O*a!VE6P5TB)ahA>D)o9Sfd`z=f5#x=N2m%E zIbna$grPR;5H}4aAk{Gywbt{T{%xqt_h1;l!ciD{((aLkSey71#-Q&hn^;|(PTU@g z;U&}_c;Lh@u_dwF_q6>|>54jrA7EKLf`#x7D#7O%fO*c?$^>C~;#iEv&Zr7Z#4@-D zwHLNKpPxZZ%;OjC!7$_$xJ?HdDs>msF&c_GJ~^m0{RjQiC*%s7u_F)GP5>}SO6(sjj-D6nbtSO`aRfHNx>$$t%~%?`VJlX}T<3yU$Scy+yU1^N zU585SFp{%5hbsA&-|es5qo_m%U9zWQ4C=kI63gIW49CAv3HV;7{yJuDXy}7c*c+EP zzQI)D-dEU2xF0WJ$v^Dxf#=wSc=c5~@NJAIeuW|U&NW-=j#!p>0%}Q@q4w0**Qmcb za_CU0c48o&M4g6PsPp{-HS_R4?ToA9K;l;Dg4;gg}u@5x~=dq zRR7BBZkyRwIp_Ug<&`B05vg<{yrFmM^Pnziuuv!rrmVKP)pes_4!0p zMP{Q0-i(^)ez()Oh4)>v!ti(h6ZRx^FjBi@g(2b+<9bAAa>2cId&toV) zMkNsR$PV1jaR~a+KOJ4T6gALpRAPrvui`UU9q*%-y!>P8uL~1s=mpUV6%WKT%*66| z2{j|{C-&9qivh$Ts8YvaIc$cS@gUUmKEVpO9<}?=Vjw<2?GfLn)L%1;dTP&qOAI6K zg)W?g;kXiO;X%}c^FFi3Gt{vy)}-H!TJ!I)8eYXH418{nWkZZ69*;`w%jeWznVqDg z3f{+9Ece2eKFM(~cA_$p$LcX^pnAsR?HjZWY6AUG6_|^)@BoHj z9uJSVKf^<@6tTNAjY>2!QO9t(6R$y)?hLAAZ%{Wx=ka*EG)=J#@xM?5OhHXx7b?+P zsOt;nwF5_@ma-|j@ZZS9+~y-1L3GS_KG=l%{yu^I@iH#J=AIsJPsJZtnD{O#ftRQU z2j{c>4N#jl5mmV?^v4OPO*{vG!9|!~=ReEKW;hOY!y?qHa}DOfW2hxMiF$=Ta(s<% zPldO~+gSxt1JuCDs0yc{5?+8saVs|KLE|6|t)X9jTbeM`=4yhvup26|G%SQ8 zQO9Ts>KHCY9m5n;;e7|VQU|8H(udGU zOHCb?I?UB1B`Y;OGbL-ZD}7*JF6xsy&=u>7h{!3E@LtyJ^exqAZ{A$ImaD^nlq}c4 zOjq`RQLfYxsp+nibXV%&jI2?vekoZgnWenV0-d2$wmr3_=REc=+Y-!4r0KezV+ AiU0rr diff --git a/languages/open-data-wizard-en_US.po b/languages/open-data-wizard-en_US.po index 60d437bf..c95dcc87 100644 --- a/languages/open-data-wizard-en_US.po +++ b/languages/open-data-wizard-en_US.po @@ -1910,8 +1910,8 @@ msgstr "+%d available" msgid "(automatisch)" msgstr "(automatic)" -msgid "Davon steuert der Wizard %d Punkte selbst bei: Das Änderungsdatum wird bei jedem Speichern automatisch gesetzt. Deshalb steht auch ein noch leerer Datensatz nicht bei 0 %%." -msgstr "Of these, the wizard contributes %d points itself: the modification date is set automatically every time you save. That is why even an empty dataset does not start at 0 %%." +msgid "Davon steuert der Wizard %d Punkte selbst bei: Das Änderungsdatum wird bei jedem Speichern automatisch gesetzt. Ein neu angelegter Datensatz steht deshalb bei 0 %% und steigt mit dem ersten Speichern — zusammen mit den Feldern, die das Formular vorbelegt (etwa die Zugriffsrechte)." +msgstr "Of these, the wizard contributes %d points by itself: the modification date is set automatically on every save. A newly created dataset therefore starts at 0 %% and rises with the first save — together with the fields the form pre-fills (the access rights, for instance)." msgid "(ohne Titel, ID %d)" msgstr "(untitled, ID %d)" diff --git a/languages/open-data-wizard.pot b/languages/open-data-wizard.pot index 97b1b563..fb803f1f 100644 --- a/languages/open-data-wizard.pot +++ b/languages/open-data-wizard.pot @@ -1531,7 +1531,7 @@ msgstr "" msgid "(automatisch)" msgstr "" -msgid "Davon steuert der Wizard %d Punkte selbst bei: Das Änderungsdatum wird bei jedem Speichern automatisch gesetzt. Deshalb steht auch ein noch leerer Datensatz nicht bei 0 %%." +msgid "Davon steuert der Wizard %d Punkte selbst bei: Das Änderungsdatum wird bei jedem Speichern automatisch gesetzt. Ein neu angelegter Datensatz steht deshalb bei 0 %% und steigt mit dem ersten Speichern — zusammen mit den Feldern, die das Formular vorbelegt (etwa die Zugriffsrechte)." msgstr "" msgid "Welchen Engagementfeldern ist dieser Datensatz zuzuordnen?" diff --git a/open-data-wizard.php b/open-data-wizard.php index d375be37..877e6841 100644 --- a/open-data-wizard.php +++ b/open-data-wizard.php @@ -3,7 +3,7 @@ * Plugin Name: Open Data Wizard * Plugin URI: https://github.com/daimpad/OpenDataWizard * Description: DCAT-AP 3.0 konforme Open Data Metadatenverwaltung für WordPress. Bereitstellung als maschinenlesbarer JSON-LD-Endpoint für offene Daten. - * Version: 2.41.0 + * Version: 2.41.1 * Requires at least: 6.4 * Requires PHP: 8.1 * Author: nozilla @@ -26,7 +26,7 @@ exit; } -define( 'ODW_VERSION', '2.41.0' ); +define( 'ODW_VERSION', '2.41.1' ); define( 'ODW_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); define( 'ODW_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); define( 'ODW_PLUGIN_FILE', __FILE__ ); diff --git a/package.json b/package.json index 53ff439d..e8fd63ed 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-data-wizard", - "version": "2.41.0", + "version": "2.41.1", "description": "WordPress plugin for publishing open data as DCAT-AP 3.0 compliant metadata", "author": "Open Data Wizard Team", "license": "GPL-2.0-or-later", diff --git a/tests/e2e/admin-workflow.spec.js b/tests/e2e/admin-workflow.spec.js index 2d2d3a2a..c57081c5 100644 --- a/tests/e2e/admin-workflow.spec.js +++ b/tests/e2e/admin-workflow.spec.js @@ -207,6 +207,17 @@ test.describe('Formular', () => { await expect(page.locator(cfField('odw_cessda_topic'))).toHaveValue(/^https?:\/\//); }); + test('ein frisch geöffnetes Formular startet bei 0 %', async ({ page }) => { + await page.goto('/wp-admin/post-new.php?post_type=odw_dataset'); + + // WordPress legt beim Öffnen einen Auto-Entwurf an und feuert dabei + // save_post. Vorher zählte der Bericht dessen Vorgabewerte mit — die + // Zugriffsrechte stehen auf „öffentlich", das Änderungsdatum wurde + // gestempelt — und das leere Formular startete bei 7 %. + const box = page.locator('#odw-quality-report'); + await expect(box.locator('.odw-quality-percent')).toHaveText('0 %'); + }); + test('wechselt auf den zweiten Reiter', async ({ page }) => { await page.goto('/wp-admin/post-new.php?post_type=odw_dataset'); diff --git a/tests/test-quality.php b/tests/test-quality.php index ef342d4b..42a01249 100644 --- a/tests/test-quality.php +++ b/tests/test-quality.php @@ -9,6 +9,41 @@ use PHPUnit\Framework\TestCase; +if ( ! class_exists( 'WP_Post' ) ) { + /** + * Minimaler WP_Post-Ersatz. + * + * ODW_Quality::calculate() und evaluate_metric() sind auf \WP_Post typisiert, + * WP_Mock bringt die Klasse aber nicht mit. Mehr als diese drei Felder liest + * der Qualitätsbericht nicht. + * + * @package OpenDataWizard + */ + class WP_Post { + + /** + * Post ID. + * + * @var int + */ + public $ID = 0; + + /** + * Post type. + * + * @var string + */ + public $post_type = 'odw_dataset'; + + /** + * Post status. + * + * @var string + */ + public $post_status = 'draft'; + } +} + /** * Unit tests for ODW_Quality. * @@ -550,4 +585,77 @@ function ( $id, $key ) { $this->assertSame( 'http://dcat-ap.de/def/licenses/cc-by/4.0', $rows[1]['license'] ); } + + // ------------------------------------------------------------------------- + // Startwert eines frisch geöffneten Formulars + // ------------------------------------------------------------------------- + + /** + * Baut ein Post-Objekt für die Bewertung. + * + * @param string $status Post-Status. + * @return \WP_Post + */ + private function post_with_status( string $status ): \WP_Post { + $post = new \WP_Post(); + $post->ID = 42; + $post->post_type = 'odw_dataset'; + $post->post_status = $status; + + return $post; + } + + /** + * Auf einem Auto-Entwurf zählt kein Wert — auch keiner, der dasteht. + * + * WordPress legt den Auto-Entwurf beim Öffnen von „Neuer Datensatz" selbst + * an. Carbon Fields liefert für ungespeicherte Felder ihren Vorgabewert + * zurück; ohne die Abfrage zählte der Bericht also, was das Formular + * vorschlägt. Derselbe Meta-Wert, einmal je Status: Nur der Entwurf zählt. + */ + public function test_evaluate_metric_counts_nothing_on_auto_draft(): void { + $this->load_class(); + + \WP_Mock::userFunction( 'get_post_meta' )->andReturn( '2026-08-27' ); + + $metric = array( + 'key' => 'modified', + 'type' => 'present', + 'check' => 'modified', + ); + + $this->assertTrue( + $this->call_private( 'evaluate_metric', $metric, $this->post_with_status( 'draft' ) ), + 'Ein gespeicherter Entwurf mit Änderungsdatum muss die Metrik erfüllen.' + ); + + $this->assertFalse( + $this->call_private( 'evaluate_metric', $metric, $this->post_with_status( 'auto-draft' ) ), + 'Auf dem Auto-Entwurf darf derselbe Wert nicht zählen.' + ); + } + + /** + * „Neuen Datensatz anlegen" startet bei 0 %, nicht bei 7 %. + * + * Die Mocks liefern absichtlich für jedes Feld einen gefüllten Wert: Der Test + * hält damit nicht bloß fest, dass ein leeres Formular 0 ergibt, sondern dass + * der Auto-Entwurf gar nichts anrechnet. Der Nenner muss stehen bleiben — + * 0 von 0 Punkten ergäbe sonst wieder eine überraschende Zahl. + */ + public function test_calculate_scores_a_fresh_form_at_zero(): void { + $this->load_class(); + + \WP_Mock::userFunction( 'get_post' )->andReturn( $this->post_with_status( 'auto-draft' ) ); + \WP_Mock::userFunction( 'carbon_get_post_meta' )->andReturn( 'irgendein Wert' ); + \WP_Mock::userFunction( 'get_post_meta' )->andReturn( '2026-08-27' ); + \WP_Mock::userFunction( 'current_time' )->andReturn( '2026-08-27 10:00:00' ); + \WP_Mock::userFunction( '__' )->andReturnArg( 0 ); + + $result = ODW_Quality::calculate( 42 ); + + $this->assertSame( 0, $result['achieved'] ); + $this->assertSame( 0, $result['score'] ); + $this->assertGreaterThan( 0, $result['assessable'], 'Die Metriken bleiben bewertbar, sie sind nur nicht erfüllt.' ); + } }