Skip to content

Commit

Permalink
feat(duplication): external page duplication
Browse files Browse the repository at this point in the history
  • Loading branch information
mrflos authored and seballot committed Jul 1, 2024
1 parent 5b4abc7 commit 301df7f
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 114 deletions.
39 changes: 20 additions & 19 deletions handlers/DuplicateHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -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}"],
Expand All @@ -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 .= '<div class="vertical-center white-bg">' . "\n"
$error .= '<div class="vertical-center white-bg">' . "\n"
. '<div class="alert alert-danger alert-error">' . "\n"
. _t('LOGIN_NOT_AUTORIZED') . '. ' . _t('LOGIN_PLEASE_REGISTER') . '.' . "\n"
. '</div>' . "\n"
Expand All @@ -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') . '.',
]);
Expand Down Expand Up @@ -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) {
Expand All @@ -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,
]);
}
}
16 changes: 12 additions & 4 deletions includes/services/DuplicationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]];
}
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
20 changes: 12 additions & 8 deletions javascripts/handlers/duplicate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}`

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
} else if (btnAction == 'edit') {
location = `${shortUrl}/?${data.pageTag}/edit`

Check warning

Code scanning / CodeQL

DOM text reinterpreted as HTML Medium

DOM text
is reinterpreted as HTML without escaping meta-characters.
} 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(`<div class="text-danger">${_t('NOT_CONNECTED')}</div>`)
// $('.login-fields').removeClass('hide')
// }
toastMessage(`${_t('ERROR')} ${jqXHR.status}`, 3000, 'alert alert-danger')
})
return false
})
Expand Down Expand Up @@ -134,4 +138,4 @@ document.addEventListener('DOMContentLoaded', () => {
toastMessage(_t('NOT_VALID_URL', { url }), 3000, 'alert alert-danger')
}
})
})
})
3 changes: 2 additions & 1 deletion lang/yeswiki_fr.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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',
];
];
78 changes: 0 additions & 78 deletions templates/handlers/duplicate-inner.twig

This file was deleted.

84 changes: 83 additions & 1 deletion templates/handlers/duplicate.twig
Original file line number Diff line number Diff line change
@@ -1,2 +1,84 @@
{% if title %}<h2>{{ title }}</h2>{% endif %}
{{ output | raw }}
{% if error %}
{{ error|raw }}
{% else %}
{% if toExternalWiki %}
{{ include_javascript('javascripts/handlers/duplicate.js') }}
<form class="duplication-wiki-form">
<div class="text-tip">{{ _t('WIKI_URL_RECENT') }}.</div>
<div class="form-group">
<label for="urlWiki" class="control-label">{{ _t('WIKI_URL') }}</label>
<div class="input-group">
<input required type="url" class="form-control" id="urlWiki" name="urlWiki" placeholder="{{ _t('WIKI_URL') }}" value="" />
<span class="input-group-btn">
<button class="btn-verify-wiki btn btn-primary" type="submit">{{ _t('VERIFY_WIKI') }}</button>
</span>
</div><!-- /input-group -->
<span id="login-message" class="help-block"></span>
</div>
</form>
<form class="login-fields hide row duplication-login-form">{# hide the field while no valid yeswiki url given #}
<div class="col-lg-4">
<div class="form-group">
<label for="username" class="control-label">{{ _t('LOGIN_WIKINAME') }}</label>
<input required type="text" class="form-control" id="username" name="username" placeholder="{{ _t('LOGIN_WIKINAME') }}" value="" />
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<label for="login" class="control-label">{{ _t('LOGIN_PASSWORD') }}</label>
<input required type="password" class="form-control" id="password" name="password" placeholder="{{ _t('LOGIN_PASSWORD') }}" value="" />
</div>
</div>
<div class="col-lg-4">
<div class="form-group">
<button type="submit" class="btn-distant-login btn btn-primary btn-block"><i class="fa fa-sign-in-alt"></i> {{ _t('DISTANT_LOGIN') }}</button>
</div>
</div>
</form>
<div class="duplication-fields hide">{# hide the field while not connected #}
{% endif %}
<form id="form-duplication" method="post" action="{{ url({handler: 'duplicate'})}}">
{% if pageTitle %}
<div class="form-group">
<label for="pageTitle" class="control-label">{{ _t('PAGE_TITLE_TO_DUPLICATE') }}</label>
<div class="input-group">
<input required type="text" class="form-control" id="pageTitle" name="pageTitle" placeholder="{{ _t('PAGE_TITLE_TO_DUPLICATE') }}" value="{{pageTitle}}" />
</div>
</div>
{% endif %}
<div class="form-group">
<label for="pageTag" class="control-label">{{ _t('PAGE_TAG_TO_DUPLICATE') }}</label>
<div class="input-group">
<span class="input-group-addon" id="baseUrl">{{ baseUrl }}</span>
<input required type="text" class="form-control" id="pageTag" name="pageTag" placeholder="{{ _t('PAGE_TAG_TO_DUPLICATE') }}" value="{{proposedTag}}" />
{% if toExternalWiki %}
<span class="input-group-btn">
<button class="btn-verify-tag btn btn-primary" type="submit">{{ _t('VERIFY_PAGE_AVAILABILITY') }}</button>
</span>
{% endif %}
</div>
<span id="pagetag-message" class="help-block"></span>
</div>
{% if attachments|length > 0 %}
<strong>{{ _t('FILES_TO_DUPLICATE') }} ({{ _t('TOTAL_SIZE') ~ ' ' ~ totalSize }})</strong>
<ol class="list-files">
{% for a in attachments %}
<li>{{a.path|replace({'files/': ''})}} ({{a.humanSize}})</li>
{% if toExternalWiki %}<input type="hidden" name="files[]" value="{{ baseUrl}}{{ a.path }}" />{% endif %}
{% endfor %}
</ol>
{% endif %}
{% if toExternalWiki %}<input type="hidden" name="pageContent" value="{{ pageContent }}" />{% endif %}

<input type="hidden" name="sourceUrl" value="{{ sourceUrl }}" />
<input type="hidden" name="originalTag" value="{{ originalTag }}" />
<input type="hidden" name="type" value="{{ type }}" />
<button name="duplicate-action" value="open" type="submit" class="btn btn-primary"><i class="fa fa-eye-open"></i>{{ _t('DUPLICATE_AND_OPEN') }}</button>
<button name="duplicate-action" value="edit" type="submit" class="btn btn-primary"><i class="fa fa-pencil-alt"></i> {{ _t('DUPLICATE_AND_EDIT') }}</button>
<button name="duplicate-action" value="return" type="submit" class="btn btn-primary"><i class="fa fa-arrow-left"></i> {{ _t('DUPLICATE_AND_RETURN') }}</button>
{% if toExternalWiki %}
</div>
{% endif %}
</form>
{% endif %}
4 changes: 2 additions & 2 deletions tools/bazar/controllers/ListController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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', [
Expand Down Expand Up @@ -129,4 +130,3 @@ public function delete($id)
$this->wiki->href('', '', [BAZ_VARIABLE_VOIR => BAZ_VOIR_LISTES], false)
);
}
}
7 changes: 6 additions & 1 deletion tools/bazar/templates/lists/list_table.twig
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
</td>
<td>{{ key }}</td>
<td>
{% if list.canDuplicate %}
<a class="btn btn-default btn-xs" href="{{ url({ params: { vue: 'listes', action: 'duplicate', idliste: key }}) }}">
<i class="far fa-clone"></i>
</a>
{% endif %}
{% if list.canEdit %}
<a class="btn btn-default btn-xs" href="{{ url({ params: { vue: 'listes', action: 'modif_liste', idliste: key }}) }}">
<i class="fa fa-pencil-alt"></i>
Expand Down Expand Up @@ -141,4 +146,4 @@
data-loading="{{ _t('BAZ_LOADING') }}"
data-existingmessage="{{ _t('BAZ_EXISTINGMESSAGE') }}"
>
</div>
</div>

0 comments on commit 301df7f

Please sign in to comment.