| 47a02a10 | 04-Jun-2026 |
Andreas Gohr <gohr@cosmocode.de> |
Parsing: make parse syntax a per-parse value, drop ModeInterface
The active parse's syntax flavour is a per-parse question, not process- global state: within a single request a plugin can render bun
Parsing: make parse syntax a per-parse value, drop ModeInterface
The active parse's syntax flavour is a per-parse question, not process- global state: within a single request a plugin can render bundled DokuWiki-syntax text inside an otherwise-Markdown page. Yet ModeRegistry was a singleton that read $conf['syntax'] and the $PARSER_MODES global, and every mode reached it through ModeRegistry::getInstance() — so the flavour lived in shared mutable state that two parses in one request would fight over.
Make the registry a short-lived value instead:
- ModeRegistry is constructed once per parse with an explicit $syntax and injected into Parser, Handler and every mode. getSyntax() / isDwPreferred() / isMdPreferred() consult $this->syntax; the DOKU_UNITTEST-gated mode-list cache hack is gone (each registry is fresh, nothing to invalidate). - p_get_instructions() is now the single place in the pipeline where $conf['syntax'] is read; from there the flavour travels as a parameter. No code under inc/Parsing/ reads $conf['syntax'] directly anymore — the five syntax-reading modes (Preformatted, GfmHr, GfmEscape, Externallink, GfmQuote) route through $this->registry.
Keep the two concepts apart, as documented in the ModeRegistry and AbstractMode docblocks: the user's configured *preference* stays in $conf['syntax'] for UI code (toolbar, settings), while the active parse's syntax is a parameter carried by the registry.
$PARSER_MODES is demoted to a deprecated, read-only mirror, published during loadPluginModes() — third-party syntax plugins (columnlist, alphalist2, phpwikify, skipentity) and the bundled info plugin read the global directly, often from their constructors, so the taxonomy must stay visible there. No core code reads the mirror.
Fold ModeInterface into AbstractMode while here: getSort()/handle() are abstract, the connect callbacks carry defaults, and the public $Lexer "FIXME should be done by setter" becomes setLexer()/getLexer() injected by Parser::addMode() alongside the registry. Nested-content resolution moves to the allowedCategories()/filterAllowedModes() hooks, resolved once when the registry is attached.
Tests build their own parser/registry through ParserTestBase::setSyntax() instead of mutating $conf and calling the removed ModeRegistry::reset().
show more ...
|
| 3e427828 | 02-Jun-2026 |
Wizzard <wizzardsk@gmail.com> |
Translation update (sk) |
| b8c2692f | 29-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
fix(httputils): build a proper internal URL for nginx X-Accel-Redirect
Unlike Apache's mod_xsendfile and lighttpd's X-LIGHTTPD-send-file, which both accept an absolute file system path and stream th
fix(httputils): build a proper internal URL for nginx X-Accel-Redirect
Unlike Apache's mod_xsendfile and lighttpd's X-LIGHTTPD-send-file, which both accept an absolute file system path and stream the file directly, nginx treats X-Accel-Redirect as a URL: it performs an internal subrequest against the value and resolves it through `internal` location blocks in its own configuration. The redirect target therefore must be a request path nginx can map back to a file, not the file system path itself.
The previous implementation blindly stripped DOKU_INC's length from the file path, which produced a broken URL whenever a data directory had been relocated outside the DokuWiki tree.
Route the file through three cases instead: files inside DOKU_INC keep their path relative to the wiki root; files in a relocated mediadir, mediaolddir, cachedir or savedir are mapped back to their default URL below data/; everything else falls through an opt-in /_x_accel_redirect/ prefix.
Path segments are URL-encoded so filenames with spaces or other special characters resolve (fixes another potential bug in the previous implementation).
Nginx configuration:
For the default layout no extra configuration is needed - files keep their path relative to the wiki root and the regular wiki location already serves them. For relocated data directories admins must add an `internal` location below the wiki's URL space pointing at the actual file system path, e.g.:
location /data/media/ { internal; alias /srv/dokuwiki-media/; }
For the /_x_accel_redirect/ fallback (used when a plugin serves files from outside the wiki and its data directories), admins opt in by adding:
location /_x_accel_redirect/ { internal; alias /; }
When the wiki is installed under a sub path, prefix both locations accordingly (e.g. /wiki/data/media/ and /wiki/_x_accel_redirect/).
fixes: #2895
show more ...
|
| 54e76260 | 29-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
chore: adjust overlong line |
| 7e687fd8 | 29-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
fix(auth): scope media ACL checks to the namespace
Media files have no per-file ACLs; permissions must be evaluated against the namespace they live in. Several call sites passed the raw media ID to
fix(auth): scope media ACL checks to the namespace
Media files have no per-file ACLs; permissions must be evaluated against the namespace they live in. Several call sites passed the raw media ID to auth_quickaclcheck(), so a page-intended exact-ID rule (e.g. on wiki:secret.png) could silently apply to a media file sharing that ID.
Introduce mediaAclPath() that builds the correct namespace wildcard path (handling root-namespace media) and route all media-related ACL checks through it. Also normalize the lone `:X` sentinel variant in fetch.functions.php to the standard `:*` form.
fixes: #4647
show more ...
|
| 6e39b4e3 | 28-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
refactor(search): extract LegacyIndexer wrapper for BC contract
Move the deprecated helpers (lookupKey, addMetaKeys, renameMetaValue, getPID, lookup) off Indexer and into a new LegacyIndexer wrapper
refactor(search): extract LegacyIndexer wrapper for BC contract
Move the deprecated helpers (lookupKey, addMetaKeys, renameMetaValue, getPID, lookup) off Indexer and into a new LegacyIndexer wrapper. The wrapper also restores the Doku_Indexer return contract (true|string) around addPage/deletePage/renamePage/clear so plugins using the legacy API keep working without try/catch.
idx_get_indexer() now returns the LegacyIndexer; getPages stays on Indexer because plugins call it directly on Indexer instances.
fixes #4645
show more ...
|
| 1dbc46ac | 27-May-2026 |
Tzipirigu <tzipirigu.radu25683@gmail.com> |
Translation update (ro) |
| 4f32c45b | 26-May-2026 |
Andreas Gohr <gohr@cosmocode.de> |
GfmLink: allow soft line break inside link text
The label character class explicitly forbade `\n`, so a CommonMark soft line break inside link text (e.g. `[link with<EOL>more](url)`) fell through to
GfmLink: allow soft line break inside link text
The label character class explicitly forbade `\n`, so a CommonMark soft line break inside link text (e.g. `[link with<EOL>more](url)`) fell through to literal text instead of producing a link. Loosen the class to accept a bare `\n` as long as it is not followed by a blank line — soft breaks are spec-allowed inside link text, blank lines are not, and refusing them also keeps `\n#`-anchored block modes (header, hr, ...) from being swallowed by a runaway link match.
The `\n` survives into the label string and renders as a literal line ending in HTML, which browsers display as a single space. This soft break behavior has been checked against https://spec.commonmark.org/dingus/
Note that this behavior differs from github where the line break is rendered as a hard break <br>.
show more ...
|
| 65dd2042 | 26-May-2026 |
Andreas Gohr <gohr@cosmocode.de> |
GfmEscape: defer \\<EOL> to DW Linebreak in mixed-syntax modes
Both GfmEscape (sort 5) and DW Linebreak (sort 140) can claim the two backslashes of `\\` followed by space/tab/newline. The lexer's ti
GfmEscape: defer \\<EOL> to DW Linebreak in mixed-syntax modes
Both GfmEscape (sort 5) and DW Linebreak (sort 140) can claim the two backslashes of `\\` followed by space/tab/newline. The lexer's tie-breaker picked GfmEscape, so DW's forced linebreak silently lost its delimiter under dw+md and md+dw. Add a negative lookahead that declines `\\[ \t\n]` whenever DW syntax is loaded — pure md keeps GFM-spec behavior. Mid-line `\\` (UNC paths etc.) still escapes.
show more ...
|
| 1536bca2 | 24-May-2026 |
Kishore D Rajegowda <kishore328@gmail.com> |
fix(locktimer): reset lock timer on paste and other input events
The lock-timer handler was bound only to `keypress`, which fires for character key presses but not for paste via right-click menu, dr
fix(locktimer): reset lock timer on paste and other input events
The lock-timer handler was bound only to `keypress`, which fires for character key presses but not for paste via right-click menu, drag-and-drop, or other non-keyboard input methods. Users editing a page solely by pasting could therefore lose their page lock after the configured timeout despite active editing.
Switch the binding to the `input` event, which fires on every value change of the textarea regardless of source (typing, paste via any method, cut, drag-drop, undo/redo). This is a superset of the previous behaviour, so no editing action regresses.
Fixes #3714
show more ...
|
| 08b5f5eb | 14-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
fix(authad): avoid warning when initAdLdap is called before opts is populated
$this->opts is initialized as an empty array, so the is_array() guard in initAdLdap() passed even when no domain had bee
fix(authad): avoid warning when initAdLdap is called before opts is populated
$this->opts is initialized as an empty array, so the is_array() guard in initAdLdap() passed even when no domain had been loaded yet, producing an "Undefined array key 'domain'" warning on PHP 8 (e.g. from getUserCount() called by the Popularity admin page).
Fixes #4501
show more ...
|
| 649695ab | 14-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
fix(infoutils): fix synology os-release file parsing
Only remove comments at the start of the line. Should be enough to make the file parsable enough for what we need.
Fixes #4572 |
| a5debd11 | 14-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
remove valid_input_set(), inline the only remaining callers
The Input::valid() method on $INPUT already provides the cleaner API originally requested in #667. The last two callers in Subscribe.php v
remove valid_input_set(), inline the only remaining callers
The Input::valid() method on $INPUT already provides the cleaner API originally requested in #667. The last two callers in Subscribe.php validated against a local $params array (not $_POST/$_GET) and need to throw a specific exception, so inline a small in_array + throw at those call sites rather than carry the legacy helper.
The class could probably be made cleaner but that's beyond the scope of this change.
Closes #667
show more ...
|
| 8c651031 | 13-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
Update copyright year in README |
| 36ba8ead | 12-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
test: add tests to catch #4637
Issue #4637 was fixed in 7686f2030b19f948d2e50df1613ef6592fa44b46 but I didn't like that no test had caught the issue.
This adds a specific test for the issue as a Ha
test: add tests to catch #4637
Issue #4637 was fixed in 7686f2030b19f948d2e50df1613ef6592fa44b46 but I didn't like that no test had caught the issue.
This adds a specific test for the issue as a HandlerTest. Additionally a simple smoke test is added that renders the wiki:syntax page and inspects the created DOM. It's not comprehensive but might help flagging similar issue in the future.
show more ...
|
| 7686f203 | 12-May-2026 |
Anna Dabrowska <dabrowska@cosmocode.de> |
Fix syntax plugin rendering
Reverse the order in which core modes and plugin modes are handled by the \dokuwiki\Parsing\Handler. Otherwise only the handle() method of the plugin is called, which is
Fix syntax plugin rendering
Reverse the order in which core modes and plugin modes are handled by the \dokuwiki\Parsing\Handler. Otherwise only the handle() method of the plugin is called, which is fine for core modes. Syntax plugins need to go through plugin() to actually add their own calls.
show more ...
|
| 8a34b0d8 | 12-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
remove comment about failing tests now that the work is complete |
| e7dae73b | 12-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
fix: apply rector and code sniffer fixes |
| d331a839 | 12-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
GFM modes: follow CATEGORY_SUBSTITION → CATEGORY_SUBSTITUTION rename
Constant was renamed on master (the typo'd 'substition' value is kept, but the constant name spells it correctly). Update GfmTabl
GFM modes: follow CATEGORY_SUBSTITION → CATEGORY_SUBSTITUTION rename
Constant was renamed on master (the typo'd 'substition' value is kept, but the constant name spells it correctly). Update GfmTable's use of the constant, plus stale docblock/comment references in GfmEscape, GfmHtmlEntity, GfmLinebreak, and GfmLinebreakTest.
show more ...
|
| 465aec67 | 12-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
Parsing tests: split LinksTest into per-ParserMode files
Replaces the monolithic LinksTest with one test class per ParserMode: CamelcaselinkTest, EmaillinkTest, ExternallinkTest, FilelinkTest, Inter
Parsing tests: split LinksTest into per-ParserMode files
Replaces the monolithic LinksTest with one test class per ParserMode: CamelcaselinkTest, EmaillinkTest, ExternallinkTest, FilelinkTest, InternallinkTest, WindowssharelinkTest. Media-parser dispatch tests move into the existing MediaTest alongside the audio/video coverage.
ExternallinkTest also adds unit coverage for the GFM autolink extension trim step: balanced parens, trailing entity refs (valid named, numeric, and unknown round-trip), non-trailing entities staying inside the URL, and mixed paren/entity peeling.
show more ...
|
| aa346d4b | 12-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
GfmSpecTest: clear acronym table in spec renderer
The default conf/acronyms.conf entries (notably FTP) get wrapped in <abbr> by the XHTML renderer's acronym() call, which the spec output never has.
GfmSpecTest: clear acronym table in spec renderer
The default conf/acronyms.conf entries (notably FTP) get wrapped in <abbr> by the XHTML renderer's acronym() call, which the spec output never has. Clearing the renderer's acronym table makes acronym() fall through to literal text, mirroring how typography substitutions are already neutralized via SpecCompatRenderer. Brings example #628 to passing without touching production wiki rendering.
show more ...
|
| 15429f02 | 12-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
Externallink: GFM autolink extension - parens and entity-ref tail
In Markdown-preferred mode, allow `(` and `)` inside URL char classes and consume an optional trailing entity reference via the shar
Externallink: GFM autolink extension - parens and entity-ref tail
In Markdown-preferred mode, allow `(` and `)` inside URL char classes and consume an optional trailing entity reference via the shared HtmlEntity::PATTERN. The Markdown-only post-processing peels off mismatched closing parens and decodes the trailing entity reference, emitting the peeled chars as cdata after the link. Refactors handle() to dispatch to handleAngleAutolink() and handleBareUrl(), with the new trim logic in peelGfmTail() and the protocol-prefix step in addProtocolPrefix(). DW-only mode behavior is unchanged.
Brings GFM spec examples #624, #625, #626 to passing.
show more ...
|
| 1beb7450 | 12-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
GfmSpecTest: skip deferred-feature spec cases
Heading-inline syntax (#36, #46) is deferred; the existing header instruction and downstream renderers would need rework to process inline modes inside
GfmSpecTest: skip deferred-feature spec cases
Heading-inline syntax (#36, #46) is deferred; the existing header instruction and downstream renderers would need rework to process inline modes inside heading content.
Bare URL autolinking without angle brackets (#619) is a deliberate DokuWiki feature in Externallink, not a feature we'll remove to match the strict CommonMark §6.8 rule.
The GFM bare-email autolink extension (#629-631) is out of scope - DokuWiki's Email mode only recognises emails inside angle brackets.
show more ...
|
| 53307a6b | 09-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
Delete inc/Search/concept.txt
The contents have been added to the wiki
|
| 73dc0a89 | 06-May-2026 |
Andreas Gohr <andi@splitbrain.org> |
fix(mail): keep '&' intact in mailto links with multiple query params
Move the email-handling helpers (obfuscate, mail_isvalid, mail_quotedprintable_encode, mail_setup) out of the procedural inc/mai
fix(mail): keep '&' intact in mailto links with multiple query params
Move the email-handling helpers (obfuscate, mail_isvalid, mail_quotedprintable_encode, mail_setup) out of the procedural inc/mail.php into a namespaced dokuwiki\MailUtils class plus a new Mailer::configInit(), and add a separate MailUtils::obfuscateUrl() for the mailto-href context.
The xhtml renderer and PluginTrait now build the link label and the href separately: the address half is run through the mailguard obfuscation, the query string is preserved verbatim with only HTML escaping applied. This fixes #1690 — in 'visible' mode the previous code rawurlencoded the entire address+query, turning '?' into '%3F' and breaking multi-parameter mailto links; in all modes the query string is no longer mangled by the [at]/[dot] substitution.
Core call sites (Mailer, auth, LegacyApiCore, common, the xhtml renderer, the parser, the bundled config/styling/usermanager plugins) are migrated to MailUtils directly. The old top-level functions and PREG_PATTERN_VALID_EMAIL constant remain as deprecated shims with rector mappings.
Tests for obfuscate / mail_isvalid / mail_quotedprintable_encode are consolidated into a single _test/tests/MailUtilsTest.php and extended with regression coverage for the multi-parameter, double-escape and URL-shape cases.
Closes #1690 Replaces #1964
show more ...
|