up template wiki

This commit is contained in:
Tykayn 2025-09-01 15:49:10 +02:00 committed by tykayn
parent 4ede0873f6
commit f96d94a414
3 changed files with 246 additions and 125 deletions

View file

@ -58,14 +58,14 @@ class WikiController extends AbstractController
$alignedSections = []; $alignedSections = [];
// First, process common sections (they already have both en and fr) // First, process common sections (they already have both en and fr)
if (isset($sectionComparison['common']) && is_array($sectionComparison['common'])) { // if (isset($sectionComparison['common']) && is_array($sectionComparison['common'])) {
foreach ($sectionComparison['common'] as $section) { // foreach ($sectionComparison['common'] as $section) {
$alignedSections[] = [ // $alignedSections[] = [
'en' => $section['en'], // 'en' => $section['en'],
'fr' => $section['fr'] // 'fr' => $section['fr']
]; // ];
} // }
} // }
// Then, process English-only sections and add empty placeholders for French // Then, process English-only sections and add empty placeholders for French
if (isset($sectionComparison['en_only']) && is_array($sectionComparison['en_only'])) { if (isset($sectionComparison['en_only']) && is_array($sectionComparison['en_only'])) {
@ -83,8 +83,8 @@ class WikiController extends AbstractController
]; ];
} }
} }
//
// Finally, process French-only sections (these will be shown at the end) // // Finally, process French-only sections (these will be shown at the end)
if (isset($sectionComparison['fr_only']) && is_array($sectionComparison['fr_only'])) { if (isset($sectionComparison['fr_only']) && is_array($sectionComparison['fr_only'])) {
foreach ($sectionComparison['fr_only'] as $section) { foreach ($sectionComparison['fr_only'] as $section) {
$alignedSections[] = [ $alignedSections[] = [
@ -121,7 +121,7 @@ class WikiController extends AbstractController
if (isset($recentChangesData['recent_changes']) && is_array($recentChangesData['recent_changes'])) { if (isset($recentChangesData['recent_changes']) && is_array($recentChangesData['recent_changes'])) {
$recentChanges = $recentChangesData['recent_changes']; $recentChanges = $recentChangesData['recent_changes'];
$lastUpdated = isset($recentChangesData['last_updated']) ? $recentChangesData['last_updated'] : null; $lastUpdated = isset($recentChangesData['last_updated']) ? $recentChangesData['last_updated'] : null;
// Process team members statistics // Process team members statistics
$teamMembers = $this->processTeamMembersStats($recentChanges); $teamMembers = $this->processTeamMembersStats($recentChanges);
} }
@ -156,22 +156,22 @@ class WikiController extends AbstractController
'team_members' => $teamMembers 'team_members' => $teamMembers
]); ]);
} }
/** /**
* Process team members statistics from recent changes data * Process team members statistics from recent changes data
* *
* @param array $recentChanges Recent changes data * @param array $recentChanges Recent changes data
* @return array Team members statistics * @return array Team members statistics
*/ */
private function processTeamMembersStats(array $recentChanges): array private function processTeamMembersStats(array $recentChanges): array
{ {
$teamMembers = []; $teamMembers = [];
// Group changes by user and count modifications // Group changes by user and count modifications
foreach ($recentChanges as $change) { foreach ($recentChanges as $change) {
$user = $change['user']; $user = $change['user'];
$changeSize = $change['change_size']; $changeSize = $change['change_size'];
// Initialize user data if not exists // Initialize user data if not exists
if (!isset($teamMembers[$user])) { if (!isset($teamMembers[$user])) {
$teamMembers[$user] = [ $teamMembers[$user] = [
@ -183,13 +183,13 @@ class WikiController extends AbstractController
'user_url' => "https://wiki.openstreetmap.org/wiki/User:" . urlencode($user) 'user_url' => "https://wiki.openstreetmap.org/wiki/User:" . urlencode($user)
]; ];
} }
// Increment contribution count // Increment contribution count
$teamMembers[$user]['contributions']++; $teamMembers[$user]['contributions']++;
// Process change size // Process change size
if (is_numeric($changeSize)) { if (is_numeric($changeSize)) {
$changeSize = (int) $changeSize; $changeSize = (int)$changeSize;
if ($changeSize > 0) { if ($changeSize > 0) {
$teamMembers[$user]['chars_added'] += $changeSize; $teamMembers[$user]['chars_added'] += $changeSize;
} elseif ($changeSize < 0) { } elseif ($changeSize < 0) {
@ -200,19 +200,19 @@ class WikiController extends AbstractController
} }
} elseif (preg_match('/^\+(\d+)$/', $changeSize, $matches)) { } elseif (preg_match('/^\+(\d+)$/', $changeSize, $matches)) {
// Format like "+123" // Format like "+123"
$teamMembers[$user]['chars_added'] += (int) $matches[1]; $teamMembers[$user]['chars_added'] += (int)$matches[1];
} elseif (preg_match('/^(\d+)$/', $changeSize, $matches)) { } elseif (preg_match('/^(\d+)$/', $changeSize, $matches)) {
// Format like "123" (note: this is not a regular minus sign) // Format like "123" (note: this is not a regular minus sign)
$teamMembers[$user]['chars_deleted'] += (int) $matches[1]; $teamMembers[$user]['chars_deleted'] += (int)$matches[1];
} }
} }
// Convert to indexed array and sort by contributions count (descending) // Convert to indexed array and sort by contributions count (descending)
$teamMembers = array_values($teamMembers); $teamMembers = array_values($teamMembers);
usort($teamMembers, function($a, $b) { usort($teamMembers, function ($a, $b) {
return $b['contributions'] - $a['contributions']; return $b['contributions'] - $a['contributions'];
}); });
return $teamMembers; return $teamMembers;
} }
@ -889,7 +889,7 @@ class WikiController extends AbstractController
$missingTranslations[$key] = $languages['en']; $missingTranslations[$key] = $languages['en'];
} }
} }
// Calculate differences between English and French versions // Calculate differences between English and French versions
foreach ($wikiPages as $key => $languages) { foreach ($wikiPages as $key => $languages) {
@ -933,7 +933,7 @@ class WikiController extends AbstractController
$pagesUnavailableInEnglish = $pagesUnavailableInEnglishData['pages']; $pagesUnavailableInEnglish = $pagesUnavailableInEnglishData['pages'];
} }
} }
// Load specific pages from outdated_pages.json // Load specific pages from outdated_pages.json
$specificPages = []; $specificPages = [];
$outdatedPagesFile = $this->getParameter('kernel.project_dir') . '/wiki_compare/outdated_pages.json'; $outdatedPagesFile = $this->getParameter('kernel.project_dir') . '/wiki_compare/outdated_pages.json';
@ -943,7 +943,7 @@ class WikiController extends AbstractController
$specificPages = $outdatedPagesData['specific_pages']; $specificPages = $outdatedPagesData['specific_pages'];
} }
} }
// Load newly created French pages // Load newly created French pages
$newlyCreatedPages = []; $newlyCreatedPages = [];
$newlyCreatedPagesFile = $this->getParameter('kernel.project_dir') . '/wiki_compare/newly_created_french_pages.json'; $newlyCreatedPagesFile = $this->getParameter('kernel.project_dir') . '/wiki_compare/newly_created_french_pages.json';
@ -1005,7 +1005,7 @@ class WikiController extends AbstractController
if (file_exists($jsonFile)) { if (file_exists($jsonFile)) {
$jsonData = json_decode(file_get_contents($jsonFile), true); $jsonData = json_decode(file_get_contents($jsonFile), true);
// Check both regular_pages and specific_pages sections // Check both regular_pages and specific_pages sections
$allPages = []; $allPages = [];
if (isset($jsonData['regular_pages']) && is_array($jsonData['regular_pages'])) { if (isset($jsonData['regular_pages']) && is_array($jsonData['regular_pages'])) {

View file

@ -13,6 +13,7 @@
<div class="card-header"> <div class="card-header">
<h2>Liste des pages wiki</h2> <h2>Liste des pages wiki</h2>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
@ -153,16 +154,16 @@
class="btn btn-sm btn-outline-info" title="Version anglaise"> class="btn btn-sm btn-outline-info" title="Version anglaise">
<i class="bi bi-flag-fill"></i> EN <i class="bi bi-flag-fill"></i> EN
</a> </a>
{# <a href="{{ path('app_admin_wiki_create_french', {'key': key}) }}" #} <a href="{{ path('app_admin_wiki_create_french', {'key': key}) }}"
{# class="btn btn-sm btn-outline-primary" #} class="btn btn-sm btn-success"
{# title="Créer une traduction française"> #} title="Créer une traduction française">
{# <i class="bi bi-translate"></i> créer FR #} <i class="bi bi-plus-circle"></i> Traduire
{# </a> #} </a>
{# <a href="{{ path('app_admin_wiki_compare', {'key': key}) }}" #} <a href="{{ path('app_admin_wiki_compare', {'key': key}) }}"
{# class="btn btn-sm btn-outline-secondary" #} class="btn btn-sm btn-outline-secondary"
{# title="Voir les détails et créer la page française"> #} title="Voir les détails et comparer">
{# <i class="bi bi-arrows-angle-expand"></i> Comparer #} <i class="bi bi-arrows-angle-expand"></i> Comparer
{# </a> #} </a>
</div> </div>
</td> </td>
</tr> </tr>
@ -180,7 +181,8 @@
<h2>Pages spécifiques ({{ specific_pages|length }})</h2> <h2>Pages spécifiques ({{ specific_pages|length }})</h2>
</div> </div>
<div class="card-body"> <div class="card-body">
<p>Ces pages wiki sont des pages spécifiques qui ont été sélectionnées pour une comparaison particulière.</p> <p>Ces pages wiki sont des pages spécifiques qui ont été sélectionnées pour une comparaison
particulière.</p>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead class="thead-dark"> <thead class="thead-dark">
@ -198,7 +200,8 @@
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
{% if page.en_page.description_img_url is defined and page.en_page.description_img_url %} {% if page.en_page.description_img_url is defined and page.en_page.description_img_url %}
<div class="me-3"> <div class="me-3">
<img src="{{ page.en_page.description_img_url }}" alt="{{ page.key }}" <img src="{{ page.en_page.description_img_url }}"
alt="{{ page.key }}"
style="max-width: 80px; max-height: 60px; object-fit: contain;"> style="max-width: 80px; max-height: 60px; object-fit: contain;">
</div> </div>
{% endif %} {% endif %}
@ -215,10 +218,10 @@
{% if page.staleness_score is defined %} {% if page.staleness_score is defined %}
<div class="progress" style="height: 20px;"> <div class="progress" style="height: 20px;">
{% set score_class = page.staleness_score > 70 ? 'bg-danger' : (page.staleness_score > 40 ? 'bg-warning' : 'bg-success') %} {% set score_class = page.staleness_score > 70 ? 'bg-danger' : (page.staleness_score > 40 ? 'bg-warning' : 'bg-success') %}
<div class="progress-bar {{ score_class }}" role="progressbar" <div class="progress-bar {{ score_class }}" role="progressbar"
style="width: {{ page.staleness_score }}%;" style="width: {{ page.staleness_score }}%;"
aria-valuenow="{{ page.staleness_score }}" aria-valuenow="{{ page.staleness_score }}"
aria-valuemin="0" aria-valuemin="0"
aria-valuemax="100"> aria-valuemax="100">
{{ page.staleness_score }} {{ page.staleness_score }}
</div> </div>
@ -277,7 +280,7 @@
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
{% if page.description_img_url is defined and page.description_img_url %} {% if page.description_img_url is defined and page.description_img_url %}
<div class="me-3"> <div class="me-3">
<img src="{{ page.description_img_url }}" alt="{{ page.title }}" <img src="{{ page.description_img_url }}" alt="{{ page.title }}"
style="max-width: 80px; max-height: 60px; object-fit: contain;"> style="max-width: 80px; max-height: 60px; object-fit: contain;">
</div> </div>
{% endif %} {% endif %}
@ -290,10 +293,10 @@
{% if page.outdatedness_score is defined %} {% if page.outdatedness_score is defined %}
<div class="progress" style="height: 20px;"> <div class="progress" style="height: 20px;">
{% set score_class = page.outdatedness_score > 70 ? 'bg-danger' : (page.outdatedness_score > 40 ? 'bg-warning' : 'bg-success') %} {% set score_class = page.outdatedness_score > 70 ? 'bg-danger' : (page.outdatedness_score > 40 ? 'bg-warning' : 'bg-success') %}
<div class="progress-bar {{ score_class }}" role="progressbar" <div class="progress-bar {{ score_class }}" role="progressbar"
style="width: {{ page.outdatedness_score }}%;" style="width: {{ page.outdatedness_score }}%;"
aria-valuenow="{{ page.outdatedness_score }}" aria-valuenow="{{ page.outdatedness_score }}"
aria-valuemin="0" aria-valuemin="0"
aria-valuemax="100"> aria-valuemax="100">
{{ page.outdatedness_score }} {{ page.outdatedness_score }}
</div> </div>
@ -331,7 +334,8 @@
<h2>Pages françaises récemment créées ({{ newly_created_pages|length }})</h2> <h2>Pages françaises récemment créées ({{ newly_created_pages|length }})</h2>
</div> </div>
<div class="card-body"> <div class="card-body">
<p>Ces pages wiki françaises ont été récemment créées et étaient auparavant dans la liste des pages non disponibles en français.</p> <p>Ces pages wiki françaises ont été récemment créées et étaient auparavant dans la liste des pages
non disponibles en français.</p>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-hover"> <table class="table table-striped table-hover">
<thead class="thead-dark"> <thead class="thead-dark">
@ -383,7 +387,7 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
<p> <p>
le score de fraîcheur prend en compte d'avantage la différence entre le nombre de mots que l'ancienneté de le score de fraîcheur prend en compte d'avantage la différence entre le nombre de mots que l'ancienneté de
modification. modification.

View file

@ -5,6 +5,46 @@
{% block body %} {% block body %}
<style> <style>
.limited-height-table {
max-height: 80vh;
overflow-y: auto;
display: block;
width: 100%;
}
.grammalecte-emphasis {
text-decoration: underline;
text-decoration-style: wavy;
text-decoration-color: #ff0766;
background: #ece5e5;
border-radius: 3px;
padding: 3px;
}
.suggestion-before, .suggestion-after {
cursor: pointer;
}
.suggestion-before:hover, .suggestion-after:hover {
color: #d1e7dd;
background: #0f5132;
}
.suggestion-correction {
color: #0f5132;
background: #d1e7dd;
background: #ccc;
border-radius: 3px;
padding: 3px;
display: block;
cursor: pointer;
}
.suggestion-correction:hover {
color: #d1e7dd;
background: #0f5132;
}
.comparaison-sections .card-body li { .comparaison-sections .card-body li {
min-height: 40px; min-height: 40px;
} }
@ -19,23 +59,23 @@
} }
.title-level-2 { .title-level-2 {
padding-left: 1.5rem; padding-right: 1.5rem;
} }
.title-level-3 { .title-level-3 {
padding-left: 2.8rem; padding-right: 2.8rem;
} }
.title-level-4 { .title-level-4 {
padding-left: 4rem; padding-right: 4rem;
} }
.title-level-5 { .title-level-5 {
padding-left: 5.2rem; padding-right: 5.2rem;
} }
.title-level-6 { .title-level-6 {
padding-left: 6.4rem; padding-right: 6.4rem;
font-size: 0.9rem; font-size: 0.9rem;
} }
@ -59,6 +99,7 @@
<h1>Comparaison Wiki OpenStreetMap - {{ key }} <h1>Comparaison Wiki OpenStreetMap - {{ key }}
{% if en_page.is_specific_page is defined and en_page.is_specific_page %} {% if en_page.is_specific_page is defined and en_page.is_specific_page %}
<a href="{{ fr_page.url|default('https://wiki.openstreetmap.org/wiki/FR:' ~ key) }}">fr</a> <a href="{{ fr_page.url|default('https://wiki.openstreetmap.org/wiki/FR:' ~ key) }}">fr</a>
<a href="{{ en_page.url }}">en</a> <a href="{{ en_page.url }}">en</a>
@ -67,6 +108,8 @@
<a href="https://wiki.openstreetmap.org/wiki/Key:{{ key }}">en</a> <a href="https://wiki.openstreetmap.org/wiki/Key:{{ key }}">en</a>
{% endif %} {% endif %}
</h1> </h1>
<p class="lead"> <p class="lead">
{% if en_page.is_specific_page is defined and en_page.is_specific_page %} {% if en_page.is_specific_page is defined and en_page.is_specific_page %}
Comparaison détaillée des pages wiki en français et en anglais pour "{{ key }}". Comparaison détaillée des pages wiki en français et en anglais pour "{{ key }}".
@ -75,7 +118,7 @@
{% endif %} {% endif %}
</p> </p>
<div class="row mb-4"> <div id="illustrations" class="row mb-4">
<div class="col-md-6"> <div class="col-md-6">
<div class="card"> <div class="card">
<div class="card-header bg-primary text-white"> <div class="card-header bg-primary text-white">
@ -108,14 +151,15 @@
</div> </div>
</div> </div>
{# suggestions de grammalecte #}
{% if fr_page is defined and fr_page is not null %} {% if fr_page is defined and fr_page is not null %}
{% if fr_page is defined and fr_page is not null and detailed_comparison['grammar_suggestions'] is defined and detailed_comparison['grammar_suggestions'] is not null and detailed_comparison['grammar_suggestions']|length > 0 %}
{% if fr_page is defined and fr_page is not null and fr_page.grammar_suggestions is defined and fr_page.grammar_suggestions is not null and fr_page.grammar_suggestions|length > 0 %}
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center"> <div class="card-header d-flex justify-content-between align-items-center">
<h2>Suggestions de corrections grammaticales</h2> <h2>Suggestions de corrections grammaticales</h2>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#allCorrectionsModal"> <button type="button" class="btn btn-primary" data-bs-toggle="modal"
data-bs-target="#allCorrectionsModal">
Voir toutes les corrections Voir toutes les corrections
</button> </button>
</div> </div>
@ -125,7 +169,7 @@
Grammalecte et peuvent contenir des erreurs.</p> Grammalecte et peuvent contenir des erreurs.</p>
</div> </div>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered limited-height-table">
<thead> <thead>
<tr> <tr>
<th>Texte</th> <th>Texte</th>
@ -134,29 +178,49 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for suggestion in fr_page.grammar_suggestions|slice(0, 5) %} {% for suggestion in detailed_comparison['grammar_suggestions'] %}
<tr> <tr>
<td> <td>
{% if suggestion.before is defined and suggestion.text is defined and suggestion.after is defined %} {% if suggestion.before is defined and suggestion.text is defined and suggestion.after is defined %}
<code>{{ suggestion.before|slice(-20)|trim }} <div>
<span class="text-danger">{{ suggestion.text }}</span> <span class="suggestion-before">
{{ suggestion.after|slice(0, 20)|trim }}</code>
{{ suggestion.before|slice(-20)|trim }}
</span>
<strong class="text-danger grammalecte-emphasis">{{ suggestion.text }}</strong>
<span class="suggestion-after">
{{ suggestion.after|slice(0, 20)|trim }}
</span>
</div>
{% endif %} {% endif %}
</td> </td>
<td> <td>
{% if suggestion.paraghraph is defined %}
<code>$ {{ suggestion.paragraph }} {{ suggestion.start }}
- {{ suggestion.end }}</code>
{% endif %}
{% if suggestion.message is defined %} {% if suggestion.message is defined %}
{{ suggestion.message }} {{ suggestion.message }}
{% endif %} {% endif %}
{# <pre> #}
{# {{ dump(suggestion) }} #}
{# </pre> #}
</td> </td>
<td> <td>
{% if suggestion.suggestions is defined and suggestion.suggestions is iterable and suggestion.suggestions|length > 0 %} {% if suggestion.suggestions is defined and suggestion.suggestions is iterable and suggestion.suggestions|length > 0 %}
<div class="dropdown"> <div class="dropdown">
<button class="btn btn-sm btn-outline-primary dropdown-toggle" type="button" id="dropdownMenuButton{{ loop.index }}" data-bs-toggle="dropdown" aria-expanded="false"> {# <button class="btn btn-sm btn-outline-primary dropdown-toggle" #}
Suggestions ({{ suggestion.suggestions|length }}) {# type="button" id="dropdownMenuButton{{ loop.index }}" #}
</button> {# data-bs-toggle="dropdown" aria-expanded="false"> #}
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton{{ loop.index }}"> {# Suggestions ({{ suggestion.suggestions|length }}) #}
{# </button> #}
<ul class="dropdown-menu-grammalecte"
aria-labelledby="dropdownMenuButton{{ loop.index }}">
{% for correction in suggestion.suggestions %} {% for correction in suggestion.suggestions %}
<li><a class="dropdown-item" href="#">{{ correction }}</a></li> <li><code class="suggestion-correction">{{ correction }}</code>
</li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
@ -168,21 +232,19 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% if fr_page.grammar_suggestions|length > 5 %}
<div class="text-center mt-3">
<p>{{ fr_page.grammar_suggestions|length - 5 }} suggestions supplémentaires disponibles</p>
</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
<!-- Modal for all corrections --> <!-- Modal for all corrections -->
<div class="modal fade" id="allCorrectionsModal" tabindex="-1" aria-labelledby="allCorrectionsModalLabel" aria-hidden="true"> <div class="modal fade" id="allCorrectionsModal" tabindex="-1" aria-labelledby="allCorrectionsModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-xl"> <div class="modal-dialog modal-xl">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title" id="allCorrectionsModalLabel">Toutes les suggestions de corrections</h5> <h5 class="modal-title" id="allCorrectionsModalLabel">Toutes les suggestions de
corrections</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
@ -196,7 +258,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for suggestion in fr_page.grammar_suggestions %} {% for suggestion in detailed_comparison['grammar_suggestions'] %}
<tr> <tr>
<td> <td>
{% if suggestion.before is defined and suggestion.text is defined and suggestion.after is defined %} {% if suggestion.before is defined and suggestion.text is defined and suggestion.after is defined %}
@ -213,12 +275,17 @@
<td> <td>
{% if suggestion.suggestions is defined and suggestion.suggestions is iterable and suggestion.suggestions|length > 0 %} {% if suggestion.suggestions is defined and suggestion.suggestions is iterable and suggestion.suggestions|length > 0 %}
<div class="dropdown"> <div class="dropdown">
<button class="btn btn-sm btn-outline-primary dropdown-toggle" type="button" id="modalDropdownMenuButton{{ loop.index }}" data-bs-toggle="dropdown" aria-expanded="false"> <button class="btn btn-sm btn-outline-primary dropdown-toggle"
type="button"
id="modalDropdownMenuButton{{ loop.index }}"
data-bs-toggle="dropdown" aria-expanded="false">
Suggestions ({{ suggestion.suggestions|length }}) Suggestions ({{ suggestion.suggestions|length }})
</button> </button>
<ul class="dropdown-menu" aria-labelledby="modalDropdownMenuButton{{ loop.index }}"> <ul class="dropdown-menu"
aria-labelledby="modalDropdownMenuButton{{ loop.index }}">
{% for correction in suggestion.suggestions %} {% for correction in suggestion.suggestions %}
<li><a class="dropdown-item" href="#">{{ correction }}</a></li> <li><a class="dropdown-item"
href="#">{{ correction }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
@ -239,6 +306,7 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
{# fin des suggestions de grammalecte #}
{% if fr_page is defined and fr_page is not null and fr_page.categories is defined and (fr_page.categories|length == 0) and en_page is defined and en_page.categories is defined and (en_page.categories|length > 0) %} {% if fr_page is defined and fr_page is not null and fr_page.categories is defined and (fr_page.categories|length == 0) and en_page is defined and en_page.categories is defined and (en_page.categories|length > 0) %}
<div class="card mb-4"> <div class="card mb-4">
@ -247,10 +315,12 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="alert alert-warning"> <div class="alert alert-warning">
<p><i class="bi bi-exclamation-triangle"></i> <strong>La page française ne contient aucune catégorie.</strong></p> <p><i class="bi bi-exclamation-triangle"></i> <strong>La page française ne contient aucune
<p>Les catégories aident à organiser les pages wiki et à les rendre plus facilement découvrables. Considérez ajouter les catégories suivantes de la page anglaise :</p> catégorie.</strong></p>
<p>Les catégories aident à organiser les pages wiki et à les rendre plus facilement
découvrables. Considérez ajouter les catégories suivantes de la page anglaise :</p>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="card"> <div class="card">
@ -277,7 +347,9 @@
<p>Pour ajouter des catégories à la page wiki :</p> <p>Pour ajouter des catégories à la page wiki :</p>
<ol> <ol>
<li>Éditez la page française sur le wiki OSM</li> <li>Éditez la page française sur le wiki OSM</li>
<li>Ajoutez les catégories à la fin de la page en utilisant la syntaxe suivante :</li> <li>Ajoutez les catégories à la fin de la page en utilisant la syntaxe suivante
:
</li>
</ol> </ol>
<div class="bg-light p-3 mb-3"> <div class="bg-light p-3 mb-3">
<code> <code>
@ -285,7 +357,8 @@
[[Category:Autre catégorie]] [[Category:Autre catégorie]]
</code> </code>
</div> </div>
<p>Vous pouvez également utiliser l'interface d'édition du wiki pour ajouter des catégories.</p> <p>Vous pouvez également utiliser l'interface d'édition du wiki pour ajouter des
catégories.</p>
<div class="d-grid gap-2 mt-3"> <div class="d-grid gap-2 mt-3">
<a href="{{ fr_page.url }}" target="_blank" class="btn btn-primary"> <a href="{{ fr_page.url }}" target="_blank" class="btn btn-primary">
<i class="bi bi-pencil-square"></i> Éditer la page française <i class="bi bi-pencil-square"></i> Éditer la page française
@ -319,27 +392,15 @@
</span> </span>
</div> </div>
<div class="card-body"> <div class="card-body">
<h4>Sections alignées par hiérarchie</h4>
<ul class="list-group mb-3"> <ul class="list-group mb-3">
{% if detailed_comparison.aligned_sections is defined and detailed_comparison.aligned_sections is iterable %} {% if detailed_comparison['section_comparison']['en_only'] is defined and detailed_comparison['section_comparison']['en_only'] is iterable %}
{% for section in detailed_comparison.aligned_sections %} {% for section in detailed_comparison['section_comparison']['en_only'] %}
{% if section.en is defined and section.en is not null %} <li class="list-group-item title-level-{{ section.level|default(1) }}">
<li class="list-group-item title-level-{{ section.en.level|default(1) }}"> <span class="badge bg-secondary pull-right">h{{ section.level|default(1) }}</span>
{# <span class="badge bg-secondary">h{{ section.en.level|default(1) }}</span> #} {% if section.title is defined and section.title is not empty %}
{% if section.en.title is defined and section.en.title is not empty %} {{ section.title }}
{{ section.en.title }} {% endif %}
{# {% elseif section.en.is_placeholder is defined and section.en.is_placeholder %} #} </li>
{# <span class="text-muted fst-italic">Section uniquement en français</span> #}
{# {% else %} #}
{# <span class="text-muted">Sans titre</span> #}
{% endif %}
{% if detailed_comparison.en_hierarchy_errors is defined and loop.index0 in detailed_comparison.en_hierarchy_errors %}
<span class="badge bg-danger ms-2"
title="Hiérarchie incorrecte">!</span>
{% endif %}
</li>
{% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
</ul> </ul>
@ -359,27 +420,15 @@
</span> </span>
</div> </div>
<div class="card-body"> <div class="card-body">
<h4>Sections alignées par hiérarchie</h4>
<ul class="list-group mb-3"> <ul class="list-group mb-3">
{% if detailed_comparison.aligned_sections is defined and detailed_comparison.aligned_sections is iterable %} {% if detailed_comparison['section_comparison']['fr_only'] is defined and detailed_comparison['section_comparison']['fr_only'] is iterable %}
{% for section in detailed_comparison.aligned_sections %} {% for section in detailed_comparison['section_comparison']['fr_only'] %}
{% if section.fr is defined and section.fr is not null %} <li class="list-group-item title-level-{{ section.level|default(1) }}">
<li class="list-group-item title-level-{{ section.fr.level|default(1) }} {% if section.fr.is_placeholder is defined and section.fr.is_placeholder %}list-group-item-warning{% endif %}"> <span class="badge bg-secondary pull-right">h{{ section.level|default(1) }}</span>
{# <span class="badge bg-secondary">h{{ section.fr.level|default(1) }}</span> #} {% if section.title is defined and section.title is not empty %}
{% if section.fr.title is defined and section.fr.title is not empty %} {{ section.title }}
{{ section.fr.title }} {% endif %}
{# {% elseif section.fr.is_placeholder is defined and section.fr.is_placeholder %} #} </li>
{# <span class="text-muted fst-italic">Section manquante à traduire</span> #}
{# {% else %} #}
{# <span class="text-muted">Sans titre</span> #}
{% endif %}
{% if detailed_comparison.fr_hierarchy_errors is defined and loop.index0 in detailed_comparison.fr_hierarchy_errors %}
<span class="badge bg-danger ms-2"
title="Hiérarchie incorrecte">!</span>
{% endif %}
</li>
{% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
</ul> </ul>
@ -898,6 +947,74 @@
}); });
}); });
}); });
// Add click event listeners to all suggestion-correction elements
document.querySelectorAll('.suggestion-correction, .suggestion-before, .suggestion-after').forEach(function (element) {
element.addEventListener('click', function () {
// Get the text content to copy
const content = this.textContent.trim();
// Copy to clipboard
navigator.clipboard.writeText(content).then(function () {
// Add a temporary class for visual feedback
element.classList.add('bg-success');
element.classList.add('text-white');
// Create and show a tooltip
const tooltip = document.createElement('div');
tooltip.textContent = 'Copié!';
tooltip.style.position = 'absolute';
tooltip.style.backgroundColor = '#28a745';
tooltip.style.color = 'white';
tooltip.style.padding = '5px 10px';
tooltip.style.borderRadius = '3px';
tooltip.style.fontSize = '12px';
tooltip.style.zIndex = '1000';
// Position the tooltip near the element
const rect = element.getBoundingClientRect();
tooltip.style.top = (window.scrollY + rect.bottom + 5) + 'px';
tooltip.style.left = (window.scrollX + rect.left) + 'px';
// Add the tooltip to the document
document.body.appendChild(tooltip);
// Remove the visual feedback and tooltip after a delay
setTimeout(function () {
element.classList.remove('bg-success');
element.classList.remove('text-white');
document.body.removeChild(tooltip);
}, 1500);
}).catch(function (err) {
console.error('Erreur lors de la copie :', err);
});
});
});
// Add click event listeners to dropdown items in the modal
document.querySelectorAll('#allCorrectionsModal .dropdown-item').forEach(function (element) {
element.addEventListener('click', function (e) {
e.preventDefault(); // Prevent the default link behavior
// Get the text content to copy
const content = this.textContent.trim();
// Copy to clipboard
navigator.clipboard.writeText(content).then(function () {
// Add a temporary class for visual feedback
element.classList.add('bg-success');
element.classList.add('text-white');
// Remove the visual feedback after a delay
setTimeout(function () {
element.classList.remove('bg-success');
element.classList.remove('text-white');
}, 1500);
}).catch(function (err) {
console.error('Erreur lors de la copie :', err);
});
});
});
}); });
</script> </script>
{% endblock %} {% endblock %}