Repository navigation
Conversation
Moves the walk over a work package's Form Configuration out of the single-WP PDF export into WorkPackage::Exports::FormConfiguration, which returns format-agnostic attribute and query groups. The PDF export and the upcoming Markdown export share it. PDF output stays the same, except that an attribute group whose attributes are all hidden from the user is no longer rendered and no longer listed in the artefact table of contents. https://community.openproject.org/wp/SE-642
The work package page hides attributes whose module is disabled in the project, such as Story Points without the Backlogs module. The single work package PDF export ignored these constraints and printed them regardless. The Form Configuration walker now iterates the group's active members for the work package's project and resolves custom fields through the work package's available custom fields, so both exports share the same rule. Groups whose attributes are all constrained away are dropped, while groups configured without attributes keep printing their title. https://community.openproject.org/wp/SE-642
myabc
force-pushed
the
feature/se-642-wp-markdown-rendering
branch
from
October 16, 2026 17:59
673736e to
6a5eec7
Compare
myabc
force-pushed
the
feature/se-642-wp-markdown-rendering
branch
from
October 16, 2026 19:04
6a5eec7 to
af92ada
Compare
myabc
commented
Oct 16, 2026
| list WorkPackage, WorkPackage::PDFExport::WorkPackageListToPdf | ||
|
|
||
| single WorkPackage, WorkPackage::PDFExport::WorkPackageToPdf | ||
| single WorkPackage, WorkPackage::Exports::Markdown |
Contributor
Author
There was a problem hiding this comment.
naming? – not currently symmetrical.
Contributor
Author
There was a problem hiding this comment.
Alternatives considered:
WorkPackage::Exports::Markdown(matches CSV; one-file exporter stays in the shared namespace). This is what I went with.WorkPackage::Exports::WorkPackageToMarkdown, mirroring the PDF class name only.WorkPackage::MarkdownExport::WorkPackageToMarkdown, mirroring the PDF layout fully; a namespace for one class.
this might be a question for you, @as-op
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
myabc
marked this pull request as ready for review
October 16, 2026 19:18
myabc
commented
Oct 16, 2026
| Rails.application.config.filter_parameters += %i[ | ||
| passw mail secret token _key crypt salt certificate otp ssn cvv cvc | ||
| ] | ||
| Rails.application.config.filter_parameters += [/\Akey\z/] |
myabc
force-pushed
the
feature/se-642-wp-markdown-rendering
branch
from
October 16, 2026 19:28
af92ada to
67976ad
Compare
Renders a work package as text/markdown at GET /work_packages/:id.md, also reachable with Accept: text/markdown and the /wp/:id.md short URL. The document follows the type's form configuration: a title heading, a Project/Status/URL header block, one section per attribute group and the description last. Attribute groups render as two-column tables, long-text custom fields as subheadings with their raw Markdown, and embedded queries as tables of the related work packages using all of the query's columns, including a group-by column. Each query cell is blanked when the user may not view that attribute on the related work package. Attribute macros are expanded in long-text fields and the description. Structural labels and headings come from the attribute and label translations. Values come from :md formatters: ISO dates and times (a lone start or due date keeps its position), Yes/No booleans, display ids and bare link URLs; hours, percentages and costs reuse the PDF unit formatters. Table cells escape backslashes and pipes and collapse CR, LF and CRLF. The controller serves the document inline with a filename disposition, the /wp redirect keeps an alphanumeric format suffix and drops any other, and the HTML work package page advertises the Markdown version with a link rel="alternate" tag. https://community.openproject.org/wp/SE-642
Registers a visibility check for the parent attribute so exports only print a parent the current user may view. Without it, a query table row, a form group or a workPackageValue:parent macro printed the subject of a parent that lives in a project the user cannot see, in both the Markdown and the PDF export. Grants the PDF spec user access to the parent's project, since that spec asserted the leaked parent. https://community.openproject.org/wp/SE-642
Lets bots fetch GET /work_packages/:id.md (or Accept: text/markdown) with an API key in the key param or the X-OpenProject-API-Key header. Adds an api_key_auth_format? predicate that gates current_api_key_user (api_request? by default; WorkPackagesController overrides it to the md format only, so a key never authenticates an HTML response chosen by content negotiation) and accept_api_key_auth :show on WorkPackagesController. find_current_user now tries the API key before the session, autologin and RSS key, so a valid key wins over a stale browser cookie. Requests authenticated by key are exempt from the session TTL check and never refresh session activity, so they neither start nor extend a session, while browser sessions on .md keep normal expiry. With header-based SSO, a mandatory header is still required, but once the key authenticated the request the header is not used to log in: no session is written and a header naming another user is ignored in favour of the key. A key-authenticated request never takes the SSO in-progress shortcut, so a retained failure cookie cannot bypass a mandatory header. Unauthenticated md requests answer 401 with WWW-Authenticate and a one-line Markdown hint. api_request? is unchanged. Removes the duplicate session_expired? in ApplicationController that shadowed the SessionLifetime concern. The API-key flag is set only for the user find_current_user accepts, so the session-expiry exemption cannot outlive a rejected key user. The key request parameter is redacted from logs. https://community.openproject.org/wp/SE-642
Instantiates the Markdown exporter with the work package only. Passing the request params as exporter options let a current_user query parameter replace the exporter's user with a string, which crashed macro expansion. Restores the attribute visibility examples that the parent check commit dropped from the attributes spec and adds the parent examples next to them. https://community.openproject.org/wp/SE-642
Adds a section to the API overview describing the text/markdown rendering of a work package. It covers the URL forms, the document structure, visibility rules, authentication with a session or the X-OpenProject-API-Key header, and the alternate link advertised on the HTML page. https://community.openproject.org/wp/SE-642
myabc
force-pushed
the
feature/se-642-wp-markdown-rendering
branch
from
October 16, 2026 19:33
67976ad to
21554d4
Compare
Lets API clients discover the Markdown document without knowing the URL convention. The link needs only view_work_packages, so it carries no export gate, and its href stays numeric like the auxiliary links. https://community.openproject.org/wp/SE-642
Lets users open the Markdown document from the menu that already offers PDF and Atom. Both menu resolvers build the URL from the display id so semantic instances land on /work_packages/<id>.md. https://community.openproject.org/wp/SE-642
Cuprite's send_keys left-clicks the receiving element before typing unless it already holds the selection. Sending Escape to body therefore clicked the viewport centre while the context menu was open. The new View Markdown entry moved the Atom item onto that point, so the BIM card view spec navigated to the Atom feed and lost its cards. Sends Escape through the driver's keyboard instead, on Cuprite and on Selenium. https://community.openproject.org/wp/SE-642
Deploying openproject with ⚡ PullPreview
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ticket
https://community.openproject.org/wp/SE-642
What are you trying to accomplish?
Bots reading a work package get Angular-rendered HTML or HAL JSON to resolve link by link. This PR adds a
text/markdowndocument shaped like the page.GET /work_packages/:id.md(also project-scoped,/wp/:id.md, andAccept: text/markdown).<link rel="alternate" type="text/markdown">, the API v3 work package exposes it as_links.markdown(numeric href, like the other auxiliary links), and the work package menu gets a "View Markdown" entry next to "Generate PDF" that opens/wp/<display id>.md.Screenshots
What approach did you choose and why?
Note
The endpoint accepts both a browser session and an API key on purpose: people open the Markdown version in the browser, bots fetch it with
X-OpenProject-API-Key. One URL serves both consumers.WorkPackage::Exports::FormConfiguration; PDF and the newWorkPackage::Exports::Markdownexporter render the same structure.parentvisibility check hides parents the user cannot see, in Markdown and PDF.allowed_to_view_attribute?, sinceQuery#resultsfilters rows, not attributes.X-OpenProject-API-Key.api_request?is untouched;showaccepts a key only for the negotiatedtext/markdownformat.find_current_usertries the key before the session cookie; keyed requests skip the session TTL and never start or refresh a session. Header SSO still requires its mandatory header, but never logs in once the key authenticated.mdrequests answer 401 withWWW-Authenticateand a one-line Markdown hint instead of a blank page. For the auth owners: that 401 branch and the key-before-session precedence are global, though only the twoaccept_api_key_authactions resolve a user from a key.?key=works, undocumented, now redacted from logs.Potential follow-ups (to be tracked separately)
AI involvement
Collaborative – AI generated a substantial part of the code; I reviewed and understand every line.
Merge checklist
docs/api/README.md)/wp/SE-642.mdin Chrome, Firefox and Safari (browsers differ on inlinetext/markdown)