Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show the strings of a translation event #198

Merged
merged 32 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
94fe2ef
Temp commit
amieiro Apr 9, 2024
77c7278
Fix typo
amieiro Apr 12, 2024
5aad306
Show multiple translation sets on a page
akirk Apr 17, 2024
10cf337
typo
akirk Apr 17, 2024
e589b8a
Use a h3 for the headlines
akirk Apr 17, 2024
704a9fc
Filter by the user
akirk Apr 17, 2024
0722fa7
Already assign the editor options upon mouse move
akirk Apr 17, 2024
3253b1c
Link the translation set
akirk Apr 17, 2024
681122d
Ensure to enqueue the wporg-translate-editor
akirk Apr 17, 2024
3599130
Add a table of contents
akirk Apr 17, 2024
0256e31
Allow toggling between waiting and all contributions
akirk Apr 17, 2024
8c0e178
styling
akirk Apr 17, 2024
fc8d700
Fix rebase error
akirk Apr 17, 2024
5681ca3
Fix lint
akirk Apr 17, 2024
450b798
Improve regex
akirk Apr 17, 2024
eb92949
Update includes/routes/event/translations.php
akirk Apr 17, 2024
ba11cbe
Update includes/routes/event/translations.php
akirk Apr 17, 2024
8ec253b
Update includes/routes/event/translations.php
akirk Apr 17, 2024
9431243
Use variables in regex
akirk Apr 17, 2024
b9e8bd3
Add missing use
akirk Apr 17, 2024
f27f39e
Move add "translations" to the URL
akirk Apr 18, 2024
315f590
Show waiting strings
akirk Apr 19, 2024
5270c2f
typo
akirk Apr 19, 2024
2f42969
Fix tests
akirk Apr 19, 2024
f2eb27d
comment
akirk Apr 19, 2024
b7514a8
Load translation helpers editor
akirk Apr 23, 2024
eee97f4
Make things work with gp-translation-helpers
akirk Apr 23, 2024
5293d10
Update templates/event-translations-header.php
akirk Apr 23, 2024
2bdf8ca
Add event translations URL
psrpinto Apr 24, 2024
75d72aa
Use Urls class
psrpinto Apr 24, 2024
43fbd2b
Remove unused code
psrpinto Apr 24, 2024
5f127d8
Merge branch 'trunk' into event-strings
akirk Apr 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions assets/css/translation-events.css
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
.event-details-stats table {
margin: 1rem;
}

.event-details-stats table {
width: 100%;
table-layout: fixed;
Expand Down Expand Up @@ -348,13 +349,19 @@ a.event-page-edit-link:hover {
border-bottom: var(--gp-color-btn-primary-bg) thin solid;
text-decoration: none;
}

ul.text-snippets {
padding: 0;
margin-left: 160px;
}
.first-time-contributor-tada::after {
content: ' 🎉';
}

ul#translation-links li {
margin-bottom: .5em;
}

.icons li .name {
display: none;
}
Expand All @@ -365,6 +372,7 @@ ul.text-snippets {
cursor: pointer;
display: inline-block;
}

/* show the event-details-right below instead of on the right on mobile */
@media (max-width: 768px) {

Expand Down
1 change: 1 addition & 0 deletions autoload.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
require_once __DIR__ . '/includes/routes/event/details.php';
require_once __DIR__ . '/includes/routes/event/edit.php';
require_once __DIR__ . '/includes/routes/event/list.php';
require_once __DIR__ . '/includes/routes/event/translations.php';
require_once __DIR__ . '/includes/routes/user/attend-event.php';
require_once __DIR__ . '/includes/routes/user/host-event.php';
require_once __DIR__ . '/includes/routes/user/my-events.php';
Expand Down
178 changes: 178 additions & 0 deletions includes/routes/event/translations.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php

namespace Wporg\TranslationEvents\Routes\Event;

use GP;
use GP_Locales;
use GP_Original;
use Translation_Entry;
use Wporg\TranslationEvents\Routes\Route;
use Wporg\TranslationEvents\Translation_Events;
use Wporg\TranslationEvents\Event\Event_Repository_Interface;

/**
* Displays the event details page.
*/
class Translations_Route extends Route {
private Event_Repository_Interface $event_repository;

public function __construct() {
parent::__construct();
$this->event_repository = Translation_Events::get_event_repository();
}

public function handle( string $event_slug, string $locale, string $status = 'any' ): void {
$user = wp_get_current_user();
$event = get_page_by_path( $event_slug, OBJECT, Translation_Events::CPT );
if ( ! $event ) {
$this->die_with_404();
}
$event = $this->event_repository->get_event( $event->ID );
if ( ! $event ) {
$this->die_with_404();
}

global $wpdb, $gp_table_prefix;

// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
$translation_sets = $wpdb->get_results(
$wpdb->prepare(
"
SELECT DISTINCT ts.id as translation_set_id, ts.name, o.project_id as project_id
FROM {$gp_table_prefix}event_actions ea
JOIN {$gp_table_prefix}originals o ON ea.original_id = o.id
JOIN {$gp_table_prefix}translation_sets ts ON o.project_id = ts.project_id AND ea.locale = ts.locale
WHERE ea.event_id = %d
AND ea.locale = %s
",
$event->id(),
$locale
)
);
$projects = array();
$translations = array();
$locale = GP_Locales::by_slug( $locale );
foreach ( $translation_sets as $ts ) {
$projects[ $ts->translation_set_id ] = GP::$project->get( $ts->project_id );

}
gp_tmpl_load( 'event-translations-header', get_defined_vars(), $this->template_path );

foreach ( $translation_sets as $ts ) {
$rows = $wpdb->get_results(
$wpdb->prepare(
"
SELECT
t.*,
o.*,
t.id as id,
o.id as original_id,
t.status as translation_status,
o.status as original_status,
t.date_added as translation_added,
o.date_added as original_added
FROM {$gp_table_prefix}event_actions ea
JOIN {$gp_table_prefix}originals o ON ea.original_id = o.id
JOIN {$gp_table_prefix}translations t ON t.original_id = ea.original_id
WHERE ea.event_id = %d
AND t.translation_set_id = %d
AND t.user_id = ea.user_id
AND t.status LIKE %s
",
$event->id(),
$ts->translation_set_id,
trim( $status, '/' ) === 'waiting' ? 'waiting' : '%'
)
);
// phpcs:enable
if ( empty( $rows ) ) {
echo '<style>li#translations_link_', esc_html( $ts->translation_set_id ), ' { display: none; }</style>';
continue;
}
$translations = array();
$project = $projects[ $ts->translation_set_id ];
$translation_set = GP::$translation_set->get( $ts->translation_set_id );
$filters = array();
$sort = array();
$glossary = GP::$glossary->get( $project->id, $locale );
$page = 1;
$per_page = 10000;
$total_translations_count = 0;
$text_direction = 'ltr';
$locale_slug = $translation_set->locale;
$translation_set_slug = $translation_set->slug;
$word_count_type = $locale->word_count_type;
$can_edit = $this->can( 'edit', 'translation-set', $translation_set->id );
$can_write = $this->can( 'write', 'project', $project->id );
$can_approve = $this->can( 'approve', 'translation-set', $translation_set->id );
$can_import_current = $can_approve;
$can_import_waiting = $can_approve || $this->can( 'import-waiting', 'translation-set', $translation_set->id );
$url = gp_url_project( $project, gp_url_join( $translation_set->locale, $translation_set->slug ) );
$set_priority_url = gp_url( '/originals/%original-id%/set_priority' );
$discard_warning_url = gp_url_project( $project, gp_url_join( $translation_set->locale, $translation_set->slug, '-discard-warning' ) );
$set_status_url = gp_url_project( $project, gp_url_join( $translation_set->locale, $translation_set->slug, '-set-status' ) );
$bulk_action = gp_url_join( $url, '-bulk' );

$editor_options[ $translation_set->id ] = compact( 'can_approve', 'can_write', 'url', 'discard_warning_url', 'set_priority_url', 'set_status_url', 'word_count_type' );

foreach ( (array) $rows as $row ) {
$row->user = null;
$row->user_last_modified = null;

if ( $row->user_id ) {
$user = get_userdata( $row->user_id );
if ( $user ) {
$row->user = (object) array(
'ID' => $user->ID,
'user_login' => $user->user_login,
'display_name' => $user->display_name,
'user_nicename' => $user->user_nicename,
);
}
}

if ( $row->user_id_last_modified ) {
$user = get_userdata( $row->user_id_last_modified );
if ( $user ) {
$row->user_last_modified = (object) array(
'ID' => $user->ID,
'user_login' => $user->user_login,
'display_name' => $user->display_name,
'user_nicename' => $user->user_nicename,
);
}
}

$row->translations = array();
for ( $i = 0; $i < $locale->nplurals; $i++ ) {
$row->translations[] = $row->{'translation_' . $i};
}
$row->references = $row->references ? preg_split( '/\s+/', $row->references, -1, PREG_SPLIT_NO_EMPTY ) : array();
$row->extracted_comments = $row->comment;
$row->warnings = $row->warnings ? maybe_unserialize( $row->warnings ) : null;
unset( $row->comment );

// Reduce range by one since we're starting at 0, see GH#516.
foreach ( range( 0, 5 ) as $i ) {
$member = "translation_$i";
unset( $row->$member );
}

$row->row_id = $row->original_id . ( $row->id ? "-$row->id" : '' );

if ( '0' !== $row->priority ) {
$row->flags = array(
'gp-priority: ' . GP_Original::$priorities[ $row->priority ],
);
}

$translations[ $row->row_id ] = new Translation_Entry( (array) $row );
}
gp_tmpl_load( 'translations', get_defined_vars(), $this->template_path );
}

gp_tmpl_load( 'event-translations-footer', get_defined_vars(), $this->template_path );
}
}
16 changes: 13 additions & 3 deletions includes/stats/stats-calculator.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
class Stats_Row {
public int $created;
public int $reviewed;
public int $waiting;
public int $users;
public ?GP_Locale $language = null;

public function __construct( $created, $reviewed, $users, ?GP_Locale $language = null ) {
public function __construct( $created, $reviewed, $waiting, $users, ?GP_Locale $language = null ) {
$this->created = $created;
$this->reviewed = $reviewed;
$this->waiting = $waiting;
$this->users = $users;
$this->language = $language;
}
Expand Down Expand Up @@ -103,8 +105,10 @@ public function for_event( int $event_id ): Event_Stats {
select locale,
sum(action = 'create') as created,
count(*) as total,
count(distinct user_id) as users
from {$gp_table_prefix}event_actions
sum(t.status = 'waiting') as waiting,
count(distinct ea.user_id) as users
from {$gp_table_prefix}event_actions ea
left join {$gp_table_prefix}translations t ON ea.original_id = t.original_id and ea.user_id = t.user_id
where event_id = %d
group by locale with rollup
",
Expand All @@ -130,9 +134,15 @@ public function for_event( int $event_id ): Event_Stats {
$lang = null;
}

if ( is_null( $row->waiting ) ) {
// The corresponding translations are missing. Could be a unit test or data corruption.
$row->waiting = 0;
}

$stats_row = new Stats_Row(
$row->created,
$row->total - $row->created,
$row->waiting,
$row->users,
$lang
);
Expand Down
4 changes: 4 additions & 0 deletions includes/urls.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ public static function event_details_absolute( int $event_id ): string {
return get_site_url() . gp_url( wp_make_link_relative( $permalink ) );
}

public static function event_translations( int $event_id, string $locale, string $status = '' ): string {
return gp_url_join( self::event_details( $event_id ), 'translations', $locale, $status );
}

public static function event_edit( int $event_id ): string {
return gp_url( '/events/edit/' . $event_id );
}
Expand Down
36 changes: 36 additions & 0 deletions templates/event-translations-footer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
</div>
<div class="clear"></div>
<script type="text/javascript">
jQuery( function($) {
var hooks_installed = {};
var current_event_translations_table = false;
<?php
foreach ( $editor_options as $translation_set_id => $options ) {
?>
$('#translations_<?php echo esc_html( $translation_set_id ); ?>' ).click( set_translation_table_<?php echo esc_html( $translation_set_id ); ?> );
$('#translations_<?php echo esc_html( $translation_set_id ); ?>' ).mousemove( function() {
if ( ! $( '#translations', this ).length ) {
set_translation_table_<?php echo esc_html( $translation_set_id ); ?>();
}
});
function set_translation_table_<?php echo esc_html( $translation_set_id ); ?>() {
if ( current_event_translations_table === <?php echo esc_html( $translation_set_id ); ?> ) {
return;
}
current_event_translations_table = <?php echo esc_html( $translation_set_id ); ?>;
$gp_editor_options = <?php echo wp_json_encode( $options ); ?>;
$( '#translations' ).attr( 'id', null );
$( '#translations_<?php echo esc_html( $translation_set_id ); ?> table' ).attr( 'id', 'translations' );
$gp.editor.table = $( '#translations' );
if ( typeof hooks_installed[<?php echo esc_html( $translation_set_id ); ?>] === 'undefined' ) {
$gp.editor.install_hooks();
hooks_installed[<?php echo esc_html( $translation_set_id ); ?>] = true;
}
$gp_translation_helpers_editor = $gp_translation_helpers_editor_<?php echo esc_html( $translation_set_id ); ?>;
}
<?php } ?>
} );
</script>
<?php
gp_enqueue_script( 'wporg-translate-editor' );
gp_tmpl_footer(); ?>
63 changes: 63 additions & 0 deletions templates/event-translations-header.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace Wporg\TranslationEvents;

use GP;
use Wporg\TranslationEvents\Event\Event;

/** @var Event $event */

/* translators: %s: Event title. */
gp_title( sprintf( __( 'Translation Events - %s' ), esc_html( $event->title() ) ) );
gp_breadcrumb_translation_events( array( '<a href="' . esc_url( Urls::event_details( $event->id() ) ) . '">' . esc_html( $event->title() ) . '</a>', __( 'Translations', 'glotpress' ), $locale->english_name ) );
gp_enqueue_scripts( array( 'gp-editor', 'gp-translations-page' ) );
wp_localize_script(
'gp-translations-page',
'$gp_translations_options',
array(
'sort' => __( 'Sort', 'glotpress' ),
'filter' => __( 'Filter', 'glotpress' ),
)
);

gp_tmpl_header();
?>

<div class="event-list-top-bar">
<h2 class="event-page-title">
<?php echo esc_html( $event->title() ); ?>
<?php if ( isset( $event ) && 'draft' === $event->status() ) : ?>
<span class="event-label-draft"><?php echo esc_html( $event->status() ); ?></span>
<?php endif; ?>
</h2>
</div>
<div class="event-page-wrapper">
<h4>
<?php
echo esc_html(
sprintf(
// Translators: %s is the locale name.
__( 'Translations to %s', 'glotpress' ),
$locale->english_name
)
);
?>
</h4>
<ul id="translation-links">
<?php foreach ( $translation_sets as $translation_set ) : ?>
<li id="translations_link_<?php echo esc_attr( $translation_set->translation_set_id ); ?>"><a href="#translations_<?php echo esc_attr( $translation_set->translation_set_id ); ?>"><?php echo esc_html( gp_project_names_from_root( $projects[ $translation_set->translation_set_id ] ) ); ?></a></li>
<?php endforeach; ?>
</ul>

<?php
if ( trim( $status, '/' ) !== 'waiting' ) {
?>
<a href="<?php echo esc_url( Urls::event_translations( $event->id(), $locale->slug, 'waiting' ) ); ?>"><?php esc_html_e( 'Show only waiting translations', 'glotpress' ); ?></a>
<?php
} else {
?>
<a href="<?php echo esc_url( Urls::event_translations( $event->id(), $locale->slug ) ); ?>"><?php esc_html_e( 'Show all contributed translations', 'glotpress' ); ?></a>
<?php
}
?>
<hr>
Loading
Loading