From 301df7ff4ae64fbac2da4f7184a99f192d7a7de0 Mon Sep 17 00:00:00 2001 From: Florian Schmitt Date: Mon, 10 Jun 2024 16:33:46 +0300 Subject: [PATCH] feat(duplication): external page duplication --- handlers/DuplicateHandler.php | 39 +++++----- includes/services/DuplicationManager.php | 16 +++- javascripts/handlers/duplicate.js | 20 +++-- lang/yeswiki_fr.php | 3 +- templates/handlers/duplicate-inner.twig | 78 ------------------- templates/handlers/duplicate.twig | 84 ++++++++++++++++++++- tools/bazar/controllers/ListController.php | 4 +- tools/bazar/templates/lists/list_table.twig | 7 +- 8 files changed, 137 insertions(+), 114 deletions(-) delete mode 100644 templates/handlers/duplicate-inner.twig diff --git a/handlers/DuplicateHandler.php b/handlers/DuplicateHandler.php index 02b3d0c80..89d3d56fb 100644 --- a/handlers/DuplicateHandler.php +++ b/handlers/DuplicateHandler.php @@ -20,10 +20,10 @@ public function run() $this->authController = $this->getService(AuthController::class); $this->entryController = $this->getService(EntryController::class); $this->duplicationManager = $this->getService(DuplicationManager::class); - $output = $title = ''; + $output = $title = $error = ''; $toExternalWiki = isset($_GET['toUrl']) && $_GET['toUrl'] == "1"; if (!$this->wiki->page) { - $output = $this->render('@templates\alert-message.twig', [ + $error .= $this->render('@templates\alert-message.twig', [ 'type' => 'warning', 'message' => str_replace( ["{beginLink}", "{endLink}"], @@ -35,10 +35,10 @@ public function run() // if no read access to the page if ($contenu = $this->getService(PageManager::class)->getOne("PageLogin")) { // si une page PageLogin existe, on l'affiche - $output .= $this->wiki->Format($contenu["body"]); + $error .= $this->wiki->Format($contenu["body"]); } else { // sinon on affiche le formulaire d'identification minimal - $output .= '
' . "\n" + $error .= '
' . "\n" . '
' . "\n" . _t('LOGIN_NOT_AUTORIZED') . '. ' . _t('LOGIN_PLEASE_REGISTER') . '.' . "\n" . '
' . "\n" @@ -52,17 +52,20 @@ public function run() if ($data['duplicate-action'] == 'edit') { $this->wiki->Redirect($this->wiki->href('edit', $data['pageTag'])); return; + } else if ($data['duplicate-action'] == 'return') { + $this->wiki->Redirect($this->wiki->href()); + return; } $this->wiki->Redirect($this->wiki->href('', $data['pageTag'])); return; } catch (\Throwable $th) { - $output = $this->render('@templates\alert-message-with-back.twig', [ + $error .= $this->render('@templates\alert-message-with-back.twig', [ 'type' => 'warning', 'message' => $th->getMessage(), ]); } } elseif (!$toExternalWiki && !$this->wiki->UserIsAdmin()) { - $output .= $this->render('@templates\alert-message-with-back.twig', [ + $error .= $this->render('@templates\alert-message-with-back.twig', [ 'type' => 'warning', 'message' => _t('ONLY_ADMINS_CAN_DUPLICATE') . '.', ]); @@ -105,18 +108,6 @@ public function run() foreach ($attachments as $a) { $totalSize = $totalSize + $a['size']; } - $output .= $this->render('@core/handlers/duplicate-inner.twig', [ - 'originalTag' => $this->wiki->GetPageTag(), - 'sourceUrl' => $this->wiki->href(), - 'proposedTag' => $proposedTag, - 'attachments' => $attachments, - 'pageTitle' => $pageTitle, - 'pageContent' => $pageContent, - 'totalSize' => $this->duplicationManager->humanFilesize($totalSize), - 'type' => $type, - 'baseUrl' => preg_replace('/\?$/Ui', '', $this->wiki->config['base_url']), - 'toExternalWiki' => $toExternalWiki, - ]); } if ($toExternalWiki) { @@ -128,7 +119,17 @@ public function run() } return $this->renderInSquelette('@core/handlers/duplicate.twig', [ 'title' => $title, - 'output' => $output, + 'originalTag' => $this->wiki->GetPageTag(), + 'error' => $error, + 'sourceUrl' => $this->wiki->href(), + 'proposedTag' => $proposedTag ?? '', + 'attachments' => $attachments ?? [], + 'pageTitle' => $pageTitle ?? '', + 'pageContent' => $pageContent ?? '', + 'totalSize' => $this->duplicationManager->humanFilesize($totalSize ?? 0), + 'type' => $type ?? '', + 'baseUrl' => preg_replace('/\?$/Ui', '', $this->wiki->config['base_url']), + 'toExternalWiki' => $toExternalWiki, ]); } } diff --git a/includes/services/DuplicationManager.php b/includes/services/DuplicationManager.php index e6802a838..92bd30e9e 100644 --- a/includes/services/DuplicationManager.php +++ b/includes/services/DuplicationManager.php @@ -108,18 +108,21 @@ public function findFilesInWikiText($tag, $wikiText) foreach ($attachments[1] as $a) { $ext = pathinfo($a, PATHINFO_EXTENSION); $filename = pathinfo($a, PATHINFO_FILENAME); - $searchPattern = '`^' . $tag . '_' . $filename . '_\d{14}_\d{14}\.' . $ext . '_?$`'; + $searchPattern = '`^' . $tag . '_' . $filename . '_\d{14}_(\d{14})\.' . $ext . '_?$`'; $path = $this->getLocalFileUploadPath(); $fh = opendir($path); while (($file = readdir($fh)) !== false) { if (strcmp($file, '.') == 0 || strcmp($file, '..') == 0 || is_dir($file)) { continue; } - if (preg_match($searchPattern, $file)) { + if (preg_match($searchPattern, $file, $matches)) { $filePath = $path . '/' . $file; $size = filesize($filePath); $humanSize = $this->humanFilesize($size); - $filesMatched[] = ['path' => $filePath, 'size' => $size, 'humanSize' => $humanSize]; + if (in_array($filename, array_keys($filesMatched)) && $matches[1] < $filesMatched[$filename]['modified']) { + continue; // we only take the latest modified version of file + } + $filesMatched[$filename] = ['path' => $filePath, 'size' => $size, 'humanSize' => $humanSize, 'modified' => $matches[1]]; } } } @@ -218,7 +221,7 @@ public function checkPostData($data) if ($page) { throw new \Exception($data['pageTag'] . ' ' . _t('ALREADY_EXISTING')); } - if (empty($data['duplicate-action']) || !in_array($data['duplicate-action'], ['open', 'edit'])) { + if (empty($data['duplicate-action']) || !in_array($data['duplicate-action'], ['open', 'edit', 'return'])) { throw new \Exception(_t('NO_DUPLICATE_ACTION') . '.'); } return $data; @@ -319,11 +322,16 @@ public function downloadFile($sourceUrl, $fromTag, $toTag, $timeoutInSec = 10) $ch = curl_init($sourceUrl); curl_setopt($ch, CURLOPT_FILE, $fp); curl_setopt($ch, CURLOPT_HEADER, 0); + // TODO: make options to allow ssl verify + curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, false); + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeoutInSec); curl_setopt($ch, CURLOPT_TIMEOUT, $timeoutInSec); curl_exec($ch); curl_close($ch); fclose($fp); + return $destPath; } diff --git a/javascripts/handlers/duplicate.js b/javascripts/handlers/duplicate.js index c821b281e..5d690d917 100644 --- a/javascripts/handlers/duplicate.js +++ b/javascripts/handlers/duplicate.js @@ -83,19 +83,23 @@ document.addEventListener('DOMContentLoaded', () => { return false }) - $('[name="duplicate-action"]').on('click', () => { + $('[name="duplicate-action"]').on('click', (e) => { + var btnAction = e.currentTarget.value $.ajax({ method: 'POST', url: `${shortUrl}/?api/pages/${$('#pageTag').val()}/duplicate`, data: $('#form-duplication').serialize() }).done((data) => { - // handleLoginResponse(data) + if (btnAction == 'open') { + location = `${shortUrl}/?${data.pageTag}` + } else if (btnAction == 'edit') { + location = `${shortUrl}/?${data.pageTag}/edit` + } else { + let url = location.href.replace(/\/duplicate.*/, '') + location = url + } }).fail((jqXHR) => { - // toastMessage(jqXHR.responseJSON.error, 3000, 'alert alert-danger') - // if (jqXHR.status === 401) { - // $('#login-message').html(`
${_t('NOT_CONNECTED')}
`) - // $('.login-fields').removeClass('hide') - // } + toastMessage(`${_t('ERROR')} ${jqXHR.status}`, 3000, 'alert alert-danger') }) return false }) @@ -134,4 +138,4 @@ document.addEventListener('DOMContentLoaded', () => { toastMessage(_t('NOT_VALID_URL', { url }), 3000, 'alert alert-danger') } }) -}) +}) \ No newline at end of file diff --git a/lang/yeswiki_fr.php b/lang/yeswiki_fr.php index 5a6a32fcc..ce319e44d 100755 --- a/lang/yeswiki_fr.php +++ b/lang/yeswiki_fr.php @@ -691,6 +691,7 @@ 'TOTAL_SIZE' => 'taille totale', 'DUPLICATE_AND_OPEN' => 'Dupliquer et afficher', 'DUPLICATE_AND_EDIT' => 'Dupliquer et éditer', + 'DUPLICATE_AND_RETURN' => 'Dupliquer et retourner sur la page source', 'NO_DUPLICATE_ACTION' => 'Pas d\'action après duplication indiquée (duplicate-action)', 'PAGE_TITLE_TO_DUPLICATE' => 'Titre après duplication', 'PAGE_TAG_TO_DUPLICATE' => 'Identifiant de la page dupliquée', @@ -704,4 +705,4 @@ 'VERIFY_PAGE_AVAILABILITY' => 'Vérifier la disponibilité de la page', 'ONLY_ADMINS_CAN_DUPLICATE' => 'Seuls les membres du groupe "admins" de ce wiki peuvent dupliquer localement', 'DISTANT_LOGIN' => 'Se connecter sur le YesWiki', -]; +]; \ No newline at end of file diff --git a/templates/handlers/duplicate-inner.twig b/templates/handlers/duplicate-inner.twig deleted file mode 100644 index f3400b2d3..000000000 --- a/templates/handlers/duplicate-inner.twig +++ /dev/null @@ -1,78 +0,0 @@ -{% if toExternalWiki %} - {{ include_javascript('javascripts/handlers/duplicate.js') }} -
-
{{ _t('WIKI_URL_RECENT') }}.
-
- -
- - - - -
- -
-
- -
{# hide the field while not connected #} - {% endif %} -
- {% if pageTitle %} -
- -
- -
-
-{% endif %} -
- -
- {{ baseUrl }} - - {% if toExternalWiki %} - - - - {% endif %} -
- -
- {% if attachments|length > 0 %} - {{ _t('FILES_TO_DUPLICATE') }} ({{ _t('TOTAL_SIZE') ~ ' ' ~ totalSize }}) -
    - {% for a in attachments %} -
  1. {{a.path|replace({'files/': ''})}} ({{a.humanSize}})
  2. - {% if toExternalWiki %}{% endif %} - {% endfor %} -
- {% endif %} - {% if toExternalWiki %}{% endif %} - - - - - - - {% if toExternalWiki %} -
- {% endif %} - diff --git a/templates/handlers/duplicate.twig b/templates/handlers/duplicate.twig index f2951df97..550499afe 100644 --- a/templates/handlers/duplicate.twig +++ b/templates/handlers/duplicate.twig @@ -1,2 +1,84 @@ {% if title %}

{{ title }}

{% endif %} -{{ output | raw }} +{% if error %} + {{ error|raw }} +{% else %} + {% if toExternalWiki %} + {{ include_javascript('javascripts/handlers/duplicate.js') }} +
+
{{ _t('WIKI_URL_RECENT') }}.
+
+ +
+ + + + +
+ +
+
+ +
{# hide the field while not connected #} + {% endif %} +
+ {% if pageTitle %} +
+ +
+ +
+
+ {% endif %} +
+ +
+ {{ baseUrl }} + + {% if toExternalWiki %} + + + + {% endif %} +
+ +
+ {% if attachments|length > 0 %} + {{ _t('FILES_TO_DUPLICATE') }} ({{ _t('TOTAL_SIZE') ~ ' ' ~ totalSize }}) +
    + {% for a in attachments %} +
  1. {{a.path|replace({'files/': ''})}} ({{a.humanSize}})
  2. + {% if toExternalWiki %}{% endif %} + {% endfor %} +
+ {% endif %} + {% if toExternalWiki %}{% endif %} + + + + + + + + {% if toExternalWiki %} +
+ {% endif %} + +{% endif %} \ No newline at end of file diff --git a/tools/bazar/controllers/ListController.php b/tools/bazar/controllers/ListController.php index 1f0e44553..df988dd53 100644 --- a/tools/bazar/controllers/ListController.php +++ b/tools/bazar/controllers/ListController.php @@ -35,7 +35,8 @@ public function displayAll() $values[$key]['title'] = $list['titre_liste']; $values[$key]['options'] = $list['label']; $values[$key]['canEdit'] = !$this->securityController->isWikiHibernated() && $this->wiki->HasAccess('write', $key); - $values[$key]['canDelete'] = !$this->securityController->isWikiHibernated() && ($this->wiki->UserIsAdmin() || $this->wiki->UserIsOwner($key)); + $values[$key]['canDelete'] = $values[$key]['canDuplicate'] = !$this->securityController->isWikiHibernated() + && ($this->wiki->UserIsAdmin() || $this->wiki->UserIsOwner($key)); } return $this->render('@bazar/lists/list_table.twig', [ @@ -129,4 +130,3 @@ public function delete($id) $this->wiki->href('', '', [BAZ_VARIABLE_VOIR => BAZ_VOIR_LISTES], false) ); } -} diff --git a/tools/bazar/templates/lists/list_table.twig b/tools/bazar/templates/lists/list_table.twig index bffe39626..cf1080aa9 100644 --- a/tools/bazar/templates/lists/list_table.twig +++ b/tools/bazar/templates/lists/list_table.twig @@ -36,6 +36,11 @@ {{ key }} + {% if list.canDuplicate %} + + + + {% endif %} {% if list.canEdit %} @@ -141,4 +146,4 @@ data-loading="{{ _t('BAZ_LOADING') }}" data-existingmessage="{{ _t('BAZ_EXISTINGMESSAGE') }}" > -
+
\ No newline at end of file