From 2ef4cda87df190a30220be5192e42c423afa873b Mon Sep 17 00:00:00 2001 From: foobarjo Date: Thu, 13 Mar 2025 15:40:29 +0100 Subject: [PATCH 01/12] Add `publish_needs_approve` option to enforce approval before publishing 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. --- action/banner.php | 80 ++++++++++++++++++++++++++++---------------- conf/default.php | 1 + conf/metadata.php | 1 + lang/de/settings.php | 2 ++ lang/en/settings.php | 2 ++ lang/it/settings.php | 2 ++ 6 files changed, 59 insertions(+), 29 deletions(-) diff --git a/action/banner.php b/action/banner.php index c6ccf42..6a9bc02 100644 --- a/action/banner.php +++ b/action/banner.php @@ -9,13 +9,19 @@ */ class action_plugin_structpublish_banner extends DokuWiki_Action_Plugin { - /** @var \helper_plugin_structpublish_db */ + /** + * @var \helper_plugin_structpublish_db + */ protected $dbHelper; - /** @var bool */ + /** + * @var bool + */ protected $compactView; - /** @inheritDoc */ + /** + * @inheritDoc + */ public function register(Doku_Event_Handler $controller) { $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'renderBanner'); @@ -69,10 +75,9 @@ public function renderBanner(Doku_Event $event) } // link to newest draft, if exists, is not shown already and user has a role - if ( - $newestRevision->getRev() != $shownRevision->getRev() && - $newestRevision->getStatus() != Constants::STATUS_PUBLISHED && - $this->dbHelper->checkAccess($ID) + if ($newestRevision->getRev() != $shownRevision->getRev() + && $newestRevision->getStatus() != Constants::STATUS_PUBLISHED + && $this->dbHelper->checkAccess($ID) ) { $banner .= $this->getBannerText('latest_draft', $newestRevision, $shownRevision->getRev()); } @@ -92,8 +97,8 @@ public function renderBanner(Doku_Event $event) /** * Fills place holder texts with data from the given Revision * - * @param string $name - * @param Revision $rev + * @param string $name + * @param Revision $rev * @return string */ protected function getBannerText($name, $rev, $diff = '') @@ -128,9 +133,9 @@ protected function getBannerText($name, $rev, $diff = '') /** * Create a HTML link to a specific revision * - * @param string $id page id - * @param int $rev revision to link to - * @param int $text the link text to use + * @param string $id page id + * @param int $rev revision to link to + * @param int $text the link text to use * @return string */ protected function makeLink($id, $rev, $text) @@ -142,44 +147,61 @@ protected function makeLink($id, $rev, $text) /** * Create the form for approval and publishing * - * @param string $status current status - * @param string $newVersion suggested new Version + * @param string $status current status + * @param string $newVersion suggested new Version * @return string */ protected function actionButtons($status, $newVersion) { + global $ID; + + // If the status is published, return an empty string if ($status === Constants::STATUS_PUBLISHED) { return ''; } + // Create a new form instance $form = new dokuwiki\Form\Form(); - if ( - $status !== Constants::STATUS_APPROVED && - $this->dbHelper->checkAccess($ID, [Constants::ACTION_APPROVE]) + + if ($status !== Constants::STATUS_APPROVED + && $this->dbHelper->checkAccess($ID, [Constants::ACTION_APPROVE]) ) { - $form->addButton( - 'structpublish[' . Constants::ACTION_APPROVE . ']', - $this->getLang('action_' . Constants::ACTION_APPROVE) - )->attr('type', 'submit'); + $form->addButton( + 'structpublish[' . Constants::ACTION_APPROVE . ']', + $this->getLang('action_' . Constants::ACTION_APPROVE) + )->attr('type', 'submit'); } - if ($this->dbHelper->checkAccess($ID, [Constants::ACTION_PUBLISH])) { - $form->addTextInput('version', $this->getLang('newversion'))->val($newVersion); - $form->addButton( - 'structpublish[' . Constants::ACTION_PUBLISH . ']', - $this->getLang('action_' . Constants::ACTION_PUBLISH) - )->attr('type', 'submit'); - } + // Add the publish button only if the status is approved and the user has access + if ((bool)$this->getConf('publish_needs_approve')) { + if ($status === Constants::STATUS_APPROVED && $this->dbHelper->checkAccess($ID, [Constants::ACTION_PUBLISH])) { + $form->addTextInput('version', $this->getLang('newversion'))->val($newVersion); + $form->addButton( + 'structpublish[' . Constants::ACTION_PUBLISH . ']', + $this->getLang('action_' . Constants::ACTION_PUBLISH) + )->attr('type', 'submit'); + } + } else { + if ($this->dbHelper->checkAccess($ID, [Constants::ACTION_PUBLISH])) { + $form->addTextInput('version', $this->getLang('newversion'))->val($newVersion); + $form->addButton( + 'structpublish[' . Constants::ACTION_PUBLISH . ']', + $this->getLang('action_' . Constants::ACTION_PUBLISH) + )->attr('type', 'submit'); + } + } + // Return the HTML representation of the form return $form->toHTML(); } + /** * Tries to increase a given version * - * @param string $version + * @param string $version * @return string */ protected function increaseVersion($version) diff --git a/conf/default.php b/conf/default.php index 8188ee3..88e2ab2 100644 --- a/conf/default.php +++ b/conf/default.php @@ -10,3 +10,4 @@ $conf['email_enable'] = 0; $conf['email_status'] = ''; $conf['compact_view'] = 0; +$conf['publish_needs_approve'] = 0; diff --git a/conf/metadata.php b/conf/metadata.php index adf5f67..0bacf8f 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -10,3 +10,4 @@ $meta['email_enable'] = ['onoff']; $meta['email_status'] = ['multicheckbox', '_other' => 'never', '_choices' => ['approve', 'publish']]; $meta['compact_view'] = ['onoff']; +$meta['publish_needs_approve'] = ['onoff']; diff --git a/lang/de/settings.php b/lang/de/settings.php index f6d41e4..28709e8 100644 --- a/lang/de/settings.php +++ b/lang/de/settings.php @@ -4,3 +4,5 @@ $lang['email_enable'] = 'E-Mails aktivieren'; $lang['email_status'] = 'Bei welchen Statusänderungen sollen E-Mails versendet werden?'; $lang['compact_view'] = 'Kompakter Banner'; +$lang['publish_needs_approve'] = 'Seiten müssen vor ihrer Veröffentlichung genehmigt werden'; + diff --git a/lang/en/settings.php b/lang/en/settings.php index 6c161b7..3b3b9a5 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -4,3 +4,5 @@ $lang['email_enable'] = 'Send emails'; $lang['email_status'] = 'Status changes that trigger emails'; $lang['compact_view'] = 'Compact banner'; +$lang['publish_needs_approve'] = 'Pages must be approved before being published'; + diff --git a/lang/it/settings.php b/lang/it/settings.php index cddfaa1..8f5315f 100644 --- a/lang/it/settings.php +++ b/lang/it/settings.php @@ -4,3 +4,5 @@ $lang['email_enable'] = 'Invia e-mail'; $lang['email_status'] = 'Modifiche dello stato che attivano le notifiche via e-mail'; $lang['compact_view'] = 'Banner compatto'; +$lang['publish_needs_approve'] = 'Le pagine devono essere approvate prima di essere pubblicate'; + From d00b3941ac06aadf76864f18ded7a6be8d7312ac Mon Sep 17 00:00:00 2001 From: foobarjo Date: Thu, 13 Mar 2025 15:48:05 +0100 Subject: [PATCH 02/12] Prevent duplicate approval or publishing of a revision on form repost, 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. --- helper/publish.php | 15 +++++++++++-- meta/Revision.php | 54 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/helper/publish.php b/helper/publish.php index fd0773f..10441e6 100644 --- a/helper/publish.php +++ b/helper/publish.php @@ -12,7 +12,9 @@ */ class helper_plugin_structpublish_publish extends DokuWiki_Plugin { - /** @var helper_plugin_structpublish_db */ + /** + * @var helper_plugin_structpublish_db + */ protected $dbHelper; public function __construct() @@ -23,7 +25,7 @@ public function __construct() /** * Save publish data * - * @param string $action + * @param string $action * @return Revision * @throws Exception */ @@ -38,6 +40,15 @@ public function saveRevision($action, $newversion = '') $revision = new Revision($ID, $INFO['currentrev']); + //do nothing if action is approve and the current revison have already been approved + if ($action === Constants::ACTION_APPROVE && $revision->getLatestApprovedRevision() !== null && $revision->getRev() == $revision->getLatestApprovedRevision()->getRev()) { + return $revision; + } + //do nothing if action is publish and the current revison have already been published + if ($action === Constants::ACTION_PUBLISH && $revision->getLatestPublishedRevision() !== null && $revision->getRev() == $revision->getLatestPublishedRevision()->getRev()) { + return $revision; + } + if ($action === Constants::ACTION_PUBLISH) { $revision->setVersion($newversion); } diff --git a/meta/Revision.php b/meta/Revision.php index 3f1e3a7..0175c7d 100644 --- a/meta/Revision.php +++ b/meta/Revision.php @@ -13,7 +13,9 @@ */ class Revision { - /** @var SQLiteDB */ + /** + * @var SQLiteDB + */ protected $sqlite; protected $schema; @@ -25,7 +27,9 @@ class Revision protected $version; protected $user; protected $datetime; - /** @var bool|\dokuwiki\plugin\struct\meta\Column */ + /** + * @var bool|\dokuwiki\plugin\struct\meta\Column + */ protected $statusCol; protected $versionCol; protected $userCol; @@ -35,8 +39,8 @@ class Revision /** * Constructor * - * @param string $id page id - * @param int $rev revision + * @param string $id page id + * @param int $rev revision */ public function __construct($id, $rev) { @@ -52,7 +56,9 @@ public function __construct($id, $rev) $this->datetimeCol = $this->schema->findColumn('datetime'); $this->revisionCol = $this->schema->findColumn('revision'); - /** @var Value[] $values */ + /** + * @var Value[] $values +*/ $values = $this->getCoreData(['revision=' . $this->rev]); if (!empty($values)) { @@ -210,7 +216,7 @@ public function getId() * Update publish status in the core table * * @param string $pid - * @param int $rid + * @param int $rid */ protected function updateCoreData($pid, $rid = 0) { @@ -234,7 +240,7 @@ protected function updateCoreData($pid, $rid = 0) * * @see https://www.dokuwiki.org/plugin:struct:filters * - * @param array $andFilters + * @param array $andFilters * @return array|Value[] */ public function getCoreData($andFilters = []) @@ -268,7 +274,7 @@ public function getCoreData($andFilters = []) * Return "latest" published revision of a given page. * If $rev is specified, "latest" means relative to the $rev revision. * - * @param int|null $rev + * @param int|null $rev * @return Revision|null */ public function getLatestPublishedRevision($rev = null) @@ -295,4 +301,36 @@ public function getLatestPublishedRevision($rev = null) return $published; } + + /** + * Return "latest" approved revision of a given page. + * If $rev is specified, "latest" means relative to the $rev revision. + * + * @param int|null $rev + * @return Revision|null + */ + public function getLatestApprovedRevision($rev = null) + { + $andFilters[] = 'status=' . Constants::STATUS_APPROVED; + if ($rev) { + $andFilters[] .= 'revision=' . $rev; + } + $latestApproved = $this->getCoreData($andFilters); + + if (empty($latestApproved)) { + return null; + } + + $approved = new Revision( + $this->id, + $latestApproved[$this->revisionCol->getColref() - 1]->getRawValue() + ); + + $approved->setStatus($latestApproved[$this->statusCol->getColref() - 1]->getRawValue()); + $approved->setUser($latestApproved[$this->userCol->getColref() - 1]->getRawValue()); + $approved->setDatetime($latestApproved[$this->datetimeCol->getColref() - 1]->getRawValue()); + $approved->setVersion($latestApproved[$this->versionCol->getColref() - 1]->getRawValue()); + + return $approved; + } } From e82ec62c9bb16715c9f97b75dad5edff7305bd8b Mon Sep 17 00:00:00 2001 From: foobarjo Date: Thu, 13 Mar 2025 15:56:02 +0100 Subject: [PATCH 03/12] DW2PDF Replacements for StructPublish 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. --- action/dw2pdf.php | 115 ++++++++++++++++++++++++++++++++++++++++++++++ lang/de/lang.php | 1 + lang/en/lang.php | 1 + lang/it/lang.php | 1 + 4 files changed, 118 insertions(+) create mode 100644 action/dw2pdf.php diff --git a/action/dw2pdf.php b/action/dw2pdf.php new file mode 100644 index 0000000..835c282 --- /dev/null +++ b/action/dw2pdf.php @@ -0,0 +1,115 @@ + + * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html + */ + +class action_plugin_structpublish_dw2pdf extends DokuWiki_Action_Plugin +{ + /** + * @var \helper_plugin_structpublish_db + */ + protected $dbHelper; + + public function register(Doku_Event_Handler $controller) + { + $controller->register_hook('PLUGIN_DW2PDF_REPLACE', 'BEFORE', $this, 'provide_structpublish_replacements'); + $controller->register_hook('PLUGIN_DW2PDF_REPLACE', 'AFTER', $this, 'clean_structpublish_replacements'); + } + + /** + * Provide StructPublish values for DW2PDF replacements + */ + public function provide_structpublish_replacements(Doku_Event $event) + { + global $ID; + global $INFO; + global $REV; + + $this->dbHelper = plugin_load('helper', 'structpublish_db'); + + if (!$this->dbHelper->isPublishable()) { + return; + } + + // get revisions + $newestRevision = new Revision($ID, $INFO['currentrev']); + if ($REV) { + $shownRevision = new Revision($ID, $REV); + } else { + $shownRevision = $newestRevision; + } + $latestpubRevision = $newestRevision->getLatestPublishedRevision(); + $prevpubRevision = $shownRevision->getLatestPublishedRevision($REV ?: $INFO['currentrev']); + $prevapprovedRevision = $shownRevision->getLatestApprovedRevision($REV ?: $INFO['currentrev']); + + //get redactor + $pageMeta = p_get_metadata($ID); + $event->data['replace']['@REDACTOR@'] = $pageMeta['last_change']['user']; + + // get last published version + if ($latestpubRevision != null) { + $event->data['replace']['@LATESTVERSION@'] = $latestpubRevision->getVersion(); + }else{ + $event->data['replace']['@LATESTVERSION@'] = $this->getLang("status_na"); + } + + // get status + $event->data['replace']['@STATUS@'] = $this->getLang("status_" . $shownRevision->getStatus()); + + // status draft + if ($event->data['replace']['@STATUS@'] === $this->getLang("status_draft")) { + $event->data['replace']['@VERSION@'] = $this->getLang("status_draft"); + $event->data['replace']['@APPROVER@'] = $this->getLang("status_na"); + $event->data['replace']['@APPROVALDATE@'] = $this->getLang("status_na"); + $event->data['replace']['@PUBLISHER@'] = $this->getLang("status_na"); + $event->data['replace']['@PUBLISHDATE@'] = $this->getLang("status_na"); + } + + // status approved + if ($event->data['replace']['@STATUS@'] === $this->getLang("status_approved")) { + $event->data['replace']['@VERSION@'] = $this->getLang("status_approved"); + $event->data['replace']['@APPROVER@'] = $shownRevision->getUser(); + $event->data['replace']['@APPROVALDATE@'] = $shownRevision->getDateTime(); + $event->data['replace']['@PUBLISHER@'] = $this->getLang("status_na"); + $event->data['replace']['@PUBLISHDATE@'] = $this->getLang("status_na"); + } + + // status published + if ($event->data['replace']['@STATUS@'] === $this->getLang("status_published")) { + $event->data['replace']['@VERSION@'] = $shownRevision->getVersion(); + $event->data['replace']['@APPROVER@'] = $prevapprovedRevision->getUser(); + $event->data['replace']['@APPROVALDATE@'] = $prevapprovedRevision->getDateTime(); + $event->data['replace']['@PUBLISHER@'] = $shownRevision->getUser(); + $event->data['replace']['@PUBLISHDATE@'] = $shownRevision->getDateTime(); + } + + } + + + /** + * Clean up replacements in DW2PDF content + */ + public function clean_structpublish_replacements(Doku_Event $event) + { + $event->data['content'] = str_replace( + ['@APPROVER@', '@APPROVALDATE@', '@PUBLISHER@', '@PUBLISHDATE@', '@VERSION@', '@STATUS@', '@REDACTOR@' , '@LATESTVERSION@'], + ['', '', '', '', '', '', '', ''], + $event->data['content'] + ); + } +} diff --git a/lang/de/lang.php b/lang/de/lang.php index 27535e0..ea545ae 100644 --- a/lang/de/lang.php +++ b/lang/de/lang.php @@ -16,6 +16,7 @@ $lang['status_draft'] = 'Entwurf'; $lang['status_approved'] = 'Zur Freigabe bereit'; $lang['status_published'] = 'Freigegeben'; +$lang['status_na'] = 'k.A.'; $lang['action_approve'] = 'Zur Freigabe bereitstellen'; $lang['action_publish'] = 'Freigeben'; diff --git a/lang/en/lang.php b/lang/en/lang.php index 3ee9718..9f01a0b 100644 --- a/lang/en/lang.php +++ b/lang/en/lang.php @@ -16,6 +16,7 @@ $lang['status_draft'] = 'Draft'; $lang['status_approved'] = 'Approved'; $lang['status_published'] = 'Published'; +$lang['status_na'] = 'N/A'; $lang['action_approve'] = 'Approve'; $lang['action_publish'] = 'Publish'; diff --git a/lang/it/lang.php b/lang/it/lang.php index 1d45a19..a12d504 100644 --- a/lang/it/lang.php +++ b/lang/it/lang.php @@ -16,6 +16,7 @@ $lang['status_draft'] = 'Bozza'; $lang['status_approved'] = 'Approvata'; $lang['status_published'] = 'Pubblicata'; +$lang['status_na'] = 'N/D'; $lang['action_approve'] = 'Approva'; $lang['action_publish'] = 'Pubblica'; From b06ec2506c10c200f761f4bde541e7e0ec37297c Mon Sep 17 00:00:00 2001 From: foobarjo Date: Thu, 13 Mar 2025 15:58:42 +0100 Subject: [PATCH 04/12] french translation --- lang/fr/lang.php | 45 ++++++++++++++++++++++++++++++++++++++++++++ lang/fr/mail.txt | 5 +++++ lang/fr/settings.php | 8 ++++++++ 3 files changed, 58 insertions(+) create mode 100644 lang/fr/lang.php create mode 100644 lang/fr/mail.txt create mode 100644 lang/fr/settings.php diff --git a/lang/fr/lang.php b/lang/fr/lang.php new file mode 100644 index 0000000..239c72f --- /dev/null +++ b/lang/fr/lang.php @@ -0,0 +1,45 @@ +brouillon en cours, créé le {revision}.'; +$lang['banner_status_approved'] = 'Cette révision de la page a été approuvée pour publication le {datetime} par {user}.'; +$lang['banner_status_published'] = 'Cette révision de la page a été publiée en tant que version "{version}" le {datetime} par {user}.'; +$lang['banner_latest_publish'] = 'La page a été publiée pour la dernière fois en tant que version {version} par {user} le {datetime}.'; +$lang['banner_previous_publish'] = 'La page a été précédemment publiée en tant que version {version} par {user} le {datetime}.'; +$lang['banner_latest_draft'] = 'Un brouillon plus récent a été créé le {revision}.'; +$lang['compact_banner_status_draft'] = 'Brouillon'; +$lang['compact_banner_status_approved'] = 'Approuvé'; +$lang['compact_banner_status_published'] = 'Publié en tant que version "{version}" le {datetime} par {user}'; +$lang['compact_banner_latest_publish'] = ''; +$lang['compact_banner_previous_publish'] = ''; +$lang['compact_banner_latest_draft'] = 'Brouillon plus récent : {revision}'; + +// admin +$lang['assign_pattern'] = 'Modèle'; +$lang['assign_user'] = 'Utilisateur ou @group'; +$lang['assign_status'] = 'Statut'; +$lang['assign_add'] = 'Ajouter une attribution'; +$lang['assign_del'] = 'Supprimer une attribution'; + +// email +$lang['email_subject'] = 'Le statut de publication d’une page wiki a changé'; +$lang['email_error_norecipients'] = 'Aucun destinataire trouvé pour la notification !'; + diff --git a/lang/fr/mail.txt b/lang/fr/mail.txt new file mode 100644 index 0000000..5080b6a --- /dev/null +++ b/lang/fr/mail.txt @@ -0,0 +1,5 @@ +Bonjour, + +Ceci est une notification pour vous informer que le statut de la page « @PAGE@ » a changé pour @STATUS_CURRENT@. + +Vous pouvez agir dessus ici : @URL@ diff --git a/lang/fr/settings.php b/lang/fr/settings.php new file mode 100644 index 0000000..2310306 --- /dev/null +++ b/lang/fr/settings.php @@ -0,0 +1,8 @@ + Date: Thu, 13 Mar 2025 15:59:50 +0100 Subject: [PATCH 05/12] spanish translation --- lang/es/lang.php | 44 ++++++++++++++++++++++++++++++++++++++++++++ lang/es/mail.txt | 6 ++++++ lang/es/settings.php | 7 +++++++ 3 files changed, 57 insertions(+) create mode 100644 lang/es/lang.php create mode 100644 lang/es/mail.txt create mode 100644 lang/es/settings.php diff --git a/lang/es/lang.php b/lang/es/lang.php new file mode 100644 index 0000000..0a87990 --- /dev/null +++ b/lang/es/lang.php @@ -0,0 +1,44 @@ +borrador en progreso, creado el {revision}.'; +$lang['banner_status_approved'] = 'Esta revisión de la página ha sido aprobada para su publicación el {datetime} por {user}.'; +$lang['banner_status_published'] = 'Esta revisión de la página ha sido publicada como versión "{version}" el {datetime} por {user}.'; +$lang['banner_latest_publish'] = 'La página fue publicada más recientemente como versión {version} por {user} el {datetime}.'; +$lang['banner_previous_publish'] = 'La página fue publicada anteriormente como versión {version} por {user} el {datetime}.'; +$lang['banner_latest_draft'] = 'Existe un borrador más reciente creado el {revision}.'; +$lang['compact_banner_status_draft'] = 'Borrador'; +$lang['compact_banner_status_approved'] = 'Aprobado'; +$lang['compact_banner_status_published'] = 'Publicado como versión "{version}" el {datetime} por {user}'; +$lang['compact_banner_latest_publish'] = ''; +$lang['compact_banner_previous_publish'] = ''; +$lang['compact_banner_latest_draft'] = 'Borrador más reciente: {revision}'; + +// administración +$lang['assign_pattern'] = 'Patrón'; +$lang['assign_user'] = 'Usuario o @grupo'; +$lang['assign_status'] = 'Estado'; +$lang['assign_add'] = 'Añadir asignación'; +$lang['assign_del'] = 'Eliminar asignación'; + +// correo electrónico +$lang['email_subject'] = 'El estado de publicación de una página wiki ha cambiado'; +$lang['email_error_norecipients'] = '¡No se encontraron destinatarios para notificar!'; diff --git a/lang/es/mail.txt b/lang/es/mail.txt new file mode 100644 index 0000000..0b5e4ed --- /dev/null +++ b/lang/es/mail.txt @@ -0,0 +1,6 @@ +Hola, + +Te informamos que el estado de la página “@PAGE@” ha cambiado a @STATUS_CURRENT@. + +Puedes tomar medidas aquí: @URL@ + diff --git a/lang/es/settings.php b/lang/es/settings.php new file mode 100644 index 0000000..3edd4a8 --- /dev/null +++ b/lang/es/settings.php @@ -0,0 +1,7 @@ + Date: Thu, 13 Mar 2025 16:02:33 +0100 Subject: [PATCH 06/12] add "email" to de/lang.php --- lang/de/lang.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lang/de/lang.php b/lang/de/lang.php index ea545ae..d90806c 100644 --- a/lang/de/lang.php +++ b/lang/de/lang.php @@ -41,3 +41,6 @@ $lang['assign_add'] = 'Hinzufügen'; $lang['assign_del'] = 'Löschen'; +// email +$lang['email_subject'] = 'Veröffentlichungsstatus einer Wikiseite hat sich geändert'; +$lang['email_error_norecipients'] = 'Keine Empfänger zum Benachrichtigen gefunden!'; From 5bfdaedb8ca9a0e3f0e5e172dad3e57d43ad3624 Mon Sep 17 00:00:00 2001 From: foobarjo Date: Thu, 13 Mar 2025 18:52:23 +0100 Subject: [PATCH 07/12] ake access and editions of revisions rely on dokuwiki's ACL instead of 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 --- action/banner.php | 4 ++-- action/save.php | 12 +++++++++--- action/show.php | 33 ++++++++++++++++++++------------- helper/db.php | 21 +++++++++++---------- plugin.info.txt | 2 +- 5 files changed, 43 insertions(+), 29 deletions(-) diff --git a/action/banner.php b/action/banner.php index 6a9bc02..7adc359 100644 --- a/action/banner.php +++ b/action/banner.php @@ -74,10 +74,10 @@ public function renderBanner(Doku_Event $event) $banner .= $this->getBannerText('previous_publish', $prevpubRevision, $shownRevision->getRev()); } - // link to newest draft, if exists, is not shown already and user has a role + // 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 - && $this->dbHelper->checkAccess($ID) + && auth_quickaclcheck($ID) >= 2 ) { $banner .= $this->getBannerText('latest_draft', $newestRevision, $shownRevision->getRev()); } diff --git a/action/save.php b/action/save.php index 8ebee3b..5712f22 100644 --- a/action/save.php +++ b/action/save.php @@ -10,7 +10,9 @@ */ class action_plugin_structpublish_save extends DokuWiki_Action_Plugin { - /** @inheritDoc */ + /** + * @inheritDoc + */ public function register(Doku_Event_Handler $controller) { $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'AFTER', $this, 'handleSave'); @@ -19,12 +21,14 @@ public function register(Doku_Event_Handler $controller) /** * Handle the page save event to store revision meta data * - * @param Doku_Event $event + * @param Doku_Event $event * @return void */ public function handleSave(Doku_Event $event) { - /** @var helper_plugin_structpublish_db $dbHelper */ + /** + * @var helper_plugin_structpublish_db $dbHelper +*/ $dbHelper = plugin_load('helper', 'structpublish_db'); $id = $event->data['id']; @@ -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()); try { $revision->save(); diff --git a/action/show.php b/action/show.php index e1c7144..cfde957 100644 --- a/action/show.php +++ b/action/show.php @@ -5,10 +5,14 @@ class action_plugin_structpublish_show extends DokuWiki_Action_Plugin { - /** @var int */ + /** + * @var int + */ protected static $latestPublishedRev; - /** @inheritDoc */ + /** + * @inheritDoc + */ public function register(Doku_Event_Handler $controller) { $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handleShow'); @@ -18,7 +22,7 @@ public function register(Doku_Event_Handler $controller) /** * Decide which revision to show based on role assignments * - * @param Doku_Event $event + * @param Doku_Event $event * @return void */ public function handleShow(Doku_Event $event) @@ -31,12 +35,13 @@ public function handleShow(Doku_Event $event) global $REV; global $INFO; - /** @var helper_plugin_structpublish_db $dbHelper */ + /** + * @var helper_plugin_structpublish_db $dbHelper +*/ $dbHelper = plugin_load('helper', 'structpublish_db'); - if ( - !$dbHelper->isPublishable() || - (auth_isadmin() && !$this->getConf('restrict_admin')) + if (!$dbHelper->isPublishable() + || (auth_isadmin() && !$this->getConf('restrict_admin')) ) { return; } @@ -44,7 +49,8 @@ public function handleShow(Doku_Event $event) $currentRevision = new Revision($ID, $REV ?: $INFO['currentrev']); $isPublished = $currentRevision->getStatus() === Constants::STATUS_PUBLISHED; - if (!$dbHelper->isPublisher($ID)) { + //Show published version or nothing to user with no Role and no ACL write + if (!$dbHelper->isPublisher($ID) && auth_quickaclcheck($ID) < 2) { $latestPublished = $currentRevision->getLatestPublishedRevision(); // there is no published revision, show nothing if (!$isPublished && is_null($latestPublished)) { @@ -67,17 +73,18 @@ public function handleShow(Doku_Event $event) * Suppress message about viewing an old revision if it is the latest one * that the current user is allowed to see. * - * @param Doku_Event $event + * @param Doku_Event $event * @return void */ public function handleShowrev(Doku_Event $event) { - /** @var helper_plugin_structpublish_db $dbHelper */ + /** + * @var helper_plugin_structpublish_db $dbHelper +*/ $dbHelper = plugin_load('helper', 'structpublish_db'); - if ( - !$dbHelper->isPublishable() || - (auth_isadmin() && !$this->getConf('restrict_admin')) + if (!$dbHelper->isPublishable() + || (auth_isadmin() && !$this->getConf('restrict_admin')) ) { return; } diff --git a/helper/db.php b/helper/db.php index 2c28159..d731839 100644 --- a/helper/db.php +++ b/helper/db.php @@ -15,7 +15,9 @@ class helper_plugin_structpublish_db extends DokuWiki_Plugin */ public function getDB() { - /** @var helper_plugin_struct_db $struct */ + /** + * @var helper_plugin_struct_db $struct +*/ $struct = plugin_load('helper', 'struct_db'); if (!$struct) { // FIXME show message? @@ -26,9 +28,8 @@ public function getDB() return null; } - // on init + // we need to access the strucpublish even if user has no role if (!$this->initialized) { - $sqlite->getPdo()->sqliteCreateFunction('IS_PUBLISHER', [$this, 'isPublisher'], -1); $this->initialized = true; } @@ -77,8 +78,8 @@ public function isPublishable($pid = null) /** * Check if the current user has the given roles on the current page * - * @param string $pid The page ID to check access for - * @param string[] $roles Roles needed. Empty for any role + * @param string $pid The page ID to check access for + * @param string[] $roles Roles needed. Empty for any role * @return bool */ public function checkAccess($pid, $roles = []) @@ -91,7 +92,7 @@ public function checkAccess($pid, $roles = []) * * Params are read via function args * - * @param ...string $pid, $userId, $groups... + * @param ...string $pid, $userId, $groups... * @return int Return an integer instead of boolean for better sqlite compatibility */ public function isPublisher() @@ -120,10 +121,10 @@ public function isPublisher() /** * Check if a given user has role assignment for a given page * - * @param string $pid Page to check - * @param string $userId User login name, current user if empty - * @param string[] $grps Groups the user has, current user's groups if empty user - * @param string[] $roles Roles the user should have, empty for any role + * @param string $pid Page to check + * @param string $userId User login name, current user if empty + * @param string[] $grps Groups the user has, current user's groups if empty user + * @param string[] $roles Roles the user should have, empty for any role * @return bool */ public static function userHasRole($pid, $userId = '', $grps = [], $roles = []) diff --git a/plugin.info.txt b/plugin.info.txt index 66b98b0..197a66c 100644 --- a/plugin.info.txt +++ b/plugin.info.txt @@ -1,7 +1,7 @@ base structpublish author Anna Dabrowska email dokuwiki@cosmocode.de -date 2024-05-20 +date 2025-03-13 name structpublish plugin desc Publishing workflows based on struct url https://dokuwiki.org/plugin:structpublish From 6e775b963906f8bed5492a656a58cc40c3462a0a Mon Sep 17 00:00:00 2001 From: moka-dev <59028680+moka-dev@users.noreply.github.com> Date: Sat, 22 Mar 2025 06:38:37 +0100 Subject: [PATCH 08/12] reload globals for bookcreator --- action/dw2pdf.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/action/dw2pdf.php b/action/dw2pdf.php index 835c282..22dce73 100644 --- a/action/dw2pdf.php +++ b/action/dw2pdf.php @@ -40,6 +40,12 @@ public function provide_structpublish_replacements(Doku_Event $event) global $INFO; global $REV; + //force reload of the globals. usefull when coming from bookcreator + $keep = $ID; + $ID = $event->data['id']; + $INFO = pageinfo(); + $REV = null; + $this->dbHelper = plugin_load('helper', 'structpublish_db'); if (!$this->dbHelper->isPublishable()) { @@ -97,10 +103,9 @@ public function provide_structpublish_replacements(Doku_Event $event) $event->data['replace']['@PUBLISHER@'] = $shownRevision->getUser(); $event->data['replace']['@PUBLISHDATE@'] = $shownRevision->getDateTime(); } - + $ID = $keep; } - /** * Clean up replacements in DW2PDF content */ From 32c60c0e00ea01198211f15ad5a2856994b6d753 Mon Sep 17 00:00:00 2001 From: moka-dev <59028680+moka-dev@users.noreply.github.com> Date: Sun, 23 Mar 2025 16:22:53 +0100 Subject: [PATCH 09/12] replacements for rev and lastpublished rev 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@ --- action/dw2pdf.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/action/dw2pdf.php b/action/dw2pdf.php index 22dce73..70e04ce 100644 --- a/action/dw2pdf.php +++ b/action/dw2pdf.php @@ -14,7 +14,7 @@ * @VERSION@ shown version * @LATESTVERSION@ latest published version * - * @author Josquin DEHAENE + * @author Josquin Dehaene * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html */ @@ -67,15 +67,18 @@ public function provide_structpublish_replacements(Doku_Event $event) $pageMeta = p_get_metadata($ID); $event->data['replace']['@REDACTOR@'] = $pageMeta['last_change']['user']; - // get last published version + // get last published version & revision if ($latestpubRevision != null) { $event->data['replace']['@LATESTVERSION@'] = $latestpubRevision->getVersion(); + $event->data['replace']['@LATESTVERSIONREVISION@'] = $latestpubRevision->getRev(); }else{ $event->data['replace']['@LATESTVERSION@'] = $this->getLang("status_na"); + $event->data['replace']['@LATESTVERSIONREVISION@'] = $this->getLang("status_na");; } - // get status + // get status and revision $event->data['replace']['@STATUS@'] = $this->getLang("status_" . $shownRevision->getStatus()); + $event->data['replace']['@REVISION@'] = $shownRevision->getRev(); // status draft if ($event->data['replace']['@STATUS@'] === $this->getLang("status_draft")) { From cebb655730c80d24330460bc65c054e46dada258 Mon Sep 17 00:00:00 2001 From: moka-dev <59028680+moka-dev@users.noreply.github.com> Date: Sun, 23 Mar 2025 16:39:48 +0100 Subject: [PATCH 10/12] Make latest published replacements more explicit and perform code cleanup --- action/dw2pdf.php | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/action/dw2pdf.php b/action/dw2pdf.php index 70e04ce..f0103b3 100644 --- a/action/dw2pdf.php +++ b/action/dw2pdf.php @@ -11,8 +11,10 @@ * @APPROVALDATE@ date of approval * @PUBLISHER@ user that published the page * @PUBLISHDATE@ date of publishing - * @VERSION@ shown version - * @LATESTVERSION@ latest published version + * @VERSION@ shown version + * @REVISION@ shown revision + * @LATESTPUBLISHEDVERSION@ latest published version + * @LATESTPUBLISHEDREVISION@ latest published revision * * @author Josquin Dehaene * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html @@ -21,7 +23,7 @@ class action_plugin_structpublish_dw2pdf extends DokuWiki_Action_Plugin { /** - * @var \helper_plugin_structpublish_db + * @var \helper_plugin_structpublish_db */ protected $dbHelper; @@ -63,17 +65,17 @@ public function provide_structpublish_replacements(Doku_Event $event) $prevpubRevision = $shownRevision->getLatestPublishedRevision($REV ?: $INFO['currentrev']); $prevapprovedRevision = $shownRevision->getLatestApprovedRevision($REV ?: $INFO['currentrev']); - //get redactor + // get redactor $pageMeta = p_get_metadata($ID); $event->data['replace']['@REDACTOR@'] = $pageMeta['last_change']['user']; - // get last published version & revision + // get lastest published version & revision if ($latestpubRevision != null) { - $event->data['replace']['@LATESTVERSION@'] = $latestpubRevision->getVersion(); - $event->data['replace']['@LATESTVERSIONREVISION@'] = $latestpubRevision->getRev(); + $event->data['replace']['@LATESTPUBLISHEDVERSION@'] = $latestpubRevision->getVersion(); + $event->data['replace']['@LATESTPUBLISHEDREVISION@'] = $latestpubRevision->getRev(); }else{ - $event->data['replace']['@LATESTVERSION@'] = $this->getLang("status_na"); - $event->data['replace']['@LATESTVERSIONREVISION@'] = $this->getLang("status_na");; + $event->data['replace']['@LATESTPUBLISHEDVERSION@'] = $this->getLang("status_na"); + $event->data['replace']['@LATESTPUBLISHEDREVISION@'] = $this->getLang("status_na");; } // get status and revision @@ -115,8 +117,8 @@ public function provide_structpublish_replacements(Doku_Event $event) public function clean_structpublish_replacements(Doku_Event $event) { $event->data['content'] = str_replace( - ['@APPROVER@', '@APPROVALDATE@', '@PUBLISHER@', '@PUBLISHDATE@', '@VERSION@', '@STATUS@', '@REDACTOR@' , '@LATESTVERSION@'], - ['', '', '', '', '', '', '', ''], + ['@APPROVER@', '@APPROVALDATE@', '@LATESTPUBLISHEDREVISION@', '@REVISION@', '@PUBLISHER@', '@PUBLISHDATE@', '@VERSION@', '@STATUS@', '@REDACTOR@' , '@LATESTPUBLISHEDVERSION@'], + ['', '', '', '', '', '', '', '', '', ''], $event->data['content'] ); } From 52bb9c060f4368cd540aef1be8556d8869e5d66f Mon Sep 17 00:00:00 2001 From: moka-dev <59028680+moka-dev@users.noreply.github.com> Date: Sun, 23 Mar 2025 19:01:21 +0100 Subject: [PATCH 11/12] Preserve provided REV when printing an older version Avoid overriding $REV value when a specific revision is requested --- action/dw2pdf.php | 1 - 1 file changed, 1 deletion(-) diff --git a/action/dw2pdf.php b/action/dw2pdf.php index f0103b3..4c1acac 100644 --- a/action/dw2pdf.php +++ b/action/dw2pdf.php @@ -46,7 +46,6 @@ public function provide_structpublish_replacements(Doku_Event $event) $keep = $ID; $ID = $event->data['id']; $INFO = pageinfo(); - $REV = null; $this->dbHelper = plugin_load('helper', 'structpublish_db'); From 88db78f8a09a33166a7241bbba6213654c1d3b2c Mon Sep 17 00:00:00 2001 From: moka-dev <59028680+moka-dev@users.noreply.github.com> Date: Sat, 29 Mar 2025 14:13:21 +0100 Subject: [PATCH 12/12] Take revision into account when exporting from BookCreator --- action/dw2pdf.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/action/dw2pdf.php b/action/dw2pdf.php index 4c1acac..8f75860 100644 --- a/action/dw2pdf.php +++ b/action/dw2pdf.php @@ -6,14 +6,14 @@ /** * Action component providing (localized) dw2pdf replacements * - * @STATUS@ draft / approved / published - * @APPROVER@ user that approved the draft - * @APPROVALDATE@ date of approval - * @PUBLISHER@ user that published the page - * @PUBLISHDATE@ date of publishing - * @VERSION@ shown version - * @REVISION@ shown revision - * @LATESTPUBLISHEDVERSION@ latest published version + * @STATUS@ draft / approved / published + * @APPROVER@ user that approved the draft + * @APPROVALDATE@ date of approval + * @PUBLISHER@ user that published the page + * @PUBLISHDATE@ date of publishing + * @VERSION@ shown version + * @REVISION@ shown revision + * @LATESTPUBLISHEDVERSION@ latest published version * @LATESTPUBLISHEDREVISION@ latest published revision * * @author Josquin Dehaene @@ -46,6 +46,7 @@ public function provide_structpublish_replacements(Doku_Event $event) $keep = $ID; $ID = $event->data['id']; $INFO = pageinfo(); + $REV = $event->data['rev']; $this->dbHelper = plugin_load('helper', 'structpublish_db');