-
Notifications
You must be signed in to change notification settings - Fork 2
Add publish_needs_approve
option to enforce approval before publishing
#27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
This PR introduces a new configuration option, `publish_needs_approve`, that enforces approval before a page can be published. When this option is enabled, the "Publish" button will only be available if the page has been approved.
…, F5, or back navigation This PR adds a safeguard to ensure that a revision is not approved or published multiple times due to form resubmission (e.g., F5 refresh or browser back navigation). Before committing to the database, the system now checks whether the page has already been approved or published.
This PR provide `dw2pdf` replacements for the `StructPublish` plugin. The following placeholders are now supported: - `@STATUS@` - Page status (`draft`, `approved`, `published`) - `@APPROVER@` - User who approved the draft - `@APPROVALDATE@` - Date of approval - `@PUBLISHER@` - User who published the page - `@PUBLISHDATE@` - Date of publishing - `@VERSION@` - Currently shown version - `@LATESTVERSION@` - Latest published version These replacements will be available when generating PDFs via DW2PDF, ensuring that the document reflects the current StructPublish metadata.
…f role Behavior Changes Editors (users with no role) can create and modify drafts in DokuWiki sections where they have ACL write permissions. Readers (users with no role) can only read published versions or see nothing in sections where they lack ACL write permissions. Changes Implemented Removed role-based checks before accessing struct data to achieve the expected banner behavior. Replaced role-based checks with ACL-based validation for access control. drafts in the database, storing user and timestamp
Add revision metadata to replacements Add the revision number of the currently shown page as @revision@ Add the revision number of the latest published version as @LATESTVERSIONREVISION@
Avoid overriding $REV value when a specific revision is requested
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, I think all of this makes sense but needs a bit clean up. The unnecessary reformatting makes it hard to review and there are multiple things mixed into one PR.
I would like to see this PR replaced by multiple smaller PRs for:
- bug fixes (one PR per fix)
- PDF Replacements
- Prevention of double submission
- New Translations
All of them without reformatting existing code and with the issues addressed in my comments
// link to newest draft, if exists, if not shown already, if user has ACL write | ||
if ($newestRevision->getRev() != $shownRevision->getRev() | ||
&& $newestRevision->getStatus() != Constants::STATUS_PUBLISHED | ||
&& auth_quickaclcheck($ID) >= 2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should use the AUTH_EDIT constant, not a number.
I assume this change is what you mentioned here:
I also modified the behavior for users with no role because I believed the original behavior was a bug. In my version, I corrected it so that users without a role can still access their pages after creation.
@moka-dev Can you explain the scenario you're addressing here?
@annda can you have a look at this change from $this->dbHelper->checkAccess()
to auth_quickaclcheck()
and chime in with your thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Previously, a user without a publish role but with write ACL could create a new page in a StructPublish-controlled section. However, once the page was saved, the user could no longer access it, which I assume is not the intended behavior. Any user with write ACL on a section should be able to create and edit pages within it—especially the ones they created—considering that approvers and publishers may be different users.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That explanation does make sense to me and I vaguely remember discussion between me and @annda about this but not the outcome. @annda I think we should discuss this and then write down what we decide on the wiki page. I think this mechanism needs to be thought through and then be implemented, tested and documented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$form->addButton( | ||
'structpublish[' . Constants::ACTION_APPROVE . ']', | ||
$this->getLang('action_' . Constants::ACTION_APPROVE) | ||
)->attr('type', 'submit'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note to self: this part seems to be the same, just reformatted
'structpublish[' . Constants::ACTION_PUBLISH . ']', | ||
$this->getLang('action_' . Constants::ACTION_PUBLISH) | ||
)->attr('type', 'submit'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The button output is the same. Makes no sense to repeat it. The code should decide if the button is to be shown or not (based on config, access and status) and then just do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok will change that
@@ -38,6 +42,8 @@ public function handleSave(Doku_Event $event) | |||
|
|||
$revision = new Revision($id, $event->data['newRevision']); | |||
$revision->setStatus(Constants::STATUS_DRAFT); | |||
$revision->setUser($_SERVER['REMOTE_USER']); | |||
$revision->setTimestamp(time()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this about? It seems to be unrelated to this PR. Is it fixing a bug? Under which circumstances? I feel like this should be a separate PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It simply updates the metadata stored in the StructPublish database. Previously, the user and timestamp were available for approved and published revisions, but not for drafts, which remained anonymous.
!$dbHelper->isPublishable() || | ||
(auth_isadmin() && !$this->getConf('restrict_admin')) | ||
if (!$dbHelper->isPublishable() | ||
|| (auth_isadmin() && !$this->getConf('restrict_admin')) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note to self: there seem to be no changes here. just formatting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I made a change on line 47. This is where we instruct StructPublish to show only the published revision of a page (or nothing) to the user, based on the ACL rather than their role. (I’ve already change the 2 for AUTH_EDIT)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$keep = $ID; | ||
$ID = $event->data['id']; | ||
$INFO = pageinfo(); | ||
$REV = $event->data['rev']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you changing the globals here? An action handler for the PDF Plugin should not change globals.
If that change is somehow necessary for rendering within a bookcreator process, that should probably be fixed in book creator or if that's not possible, at a proper place in dw2pdf, struct or structpublish (depending on who is at fault). But it should not be a side effect of handling PDF generation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When coming from BookCreator, $ID refers to the BookCreator page, so we cannot retrieve the metadata of the page currently being rendered. This is why we need to manually initialize the variables.
|
||
// get redactor | ||
$pageMeta = p_get_metadata($ID); | ||
$event->data['replace']['@REDACTOR@'] = $pageMeta['last_change']['user']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redactor is the one who approved the the change? I am not opposed to the term but I don't think we use it anywhere else. Might make sense to streamline the naming.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps the variable name could be improved—for example, $EDITOR for clarity—as it refers to the last editor of the page. approver and publisher are potentially different users
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, then "editor" is the better name.
// get lastest published version & revision | ||
if ($latestpubRevision != null) { | ||
$event->data['replace']['@LATESTPUBLISHEDVERSION@'] = $latestpubRevision->getVersion(); | ||
$event->data['replace']['@LATESTPUBLISHEDREVISION@'] = $latestpubRevision->getRev(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a couple of underscores for better readability, but that's nitpicking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
$event->data['replace']['@APPROVALDATE@'] = $prevapprovedRevision->getDateTime(); | ||
$event->data['replace']['@PUBLISHER@'] = $shownRevision->getUser(); | ||
$event->data['replace']['@PUBLISHDATE@'] = $shownRevision->getDateTime(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also prefix all variables with STRUCTAPPROVE_
to avoid any accidental clashes with other plugins.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
['@APPROVER@', '@APPROVALDATE@', '@LATESTPUBLISHEDREVISION@', '@REVISION@', '@PUBLISHER@', '@PUBLISHDATE@', '@VERSION@', '@STATUS@', '@REDACTOR@' , '@LATESTPUBLISHEDVERSION@'], | ||
['', '', '', '', '', '', '', '', '', ''], | ||
$event->data['content'] | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this handler. Why is is it necessary? Shouldn't the BEFORE handler already handle all of those?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This clears the StructPublish variables to ensure they are not accidentally reused where they shouldn’t be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it runs a second replace operation on the page content. It does not clear any variables.
What exactly is the issue you're trying to fix here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might have miss something, but I was thinking this could clean up the replacements since it’s called on an AFTER hook:
$controller->register_hook('PLUGIN_DW2PDF_REPLACE', 'AFTER', $this, 'clean_structpublish_replacements');
This PR introduces a new configuration option,
publish_needs_approve
, that enforces approval before a page can be published. When this option is enabled, the "Publish" button will only be available if the page has been approved.