<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[13683] sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events: Translate: Sync "Translation Events" from GitHub</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { white-space: pre-line; overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://meta.trac.wordpress.org/changeset/13683">13683</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://meta.trac.wordpress.org/changeset/13683","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>amieiro</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2024-05-09 08:33:51 +0000 (Thu, 09 May 2024)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>Translate: Sync "Translation Events" from GitHub</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsassetscsstranslationeventscss">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/css/translation-events.css</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsassetsjstranslationeventsjs">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/js/translation-events.js</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsautoloadphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/autoload.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesattendeeattendeerepositoryphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee-repository.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesattendeeattendeephp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventformhandlerphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-form-handler.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventrepositorycachedphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-cached.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventrepositoryinterfacephp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-interface.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventrepositoryphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventcreatephp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/create.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventdetailsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/details.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventeditphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/edit.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesroutesuserattendeventphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/attend-event.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesroutesuserhosteventphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/host-event.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesstatsstatscalculatorphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/stats/stats-calculator.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesupgradephp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/upgrade.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventsformphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-form.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventsheaderphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-header.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventslistphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-list.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventsmyeventsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-my-events.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateshelperfunctionsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/helper-functions.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventswporggptranslationeventsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/wporg-gp-translation-events.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventcapabilitiesphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-capabilities.php</a></li>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/notifications/</li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesnotificationsnotificationsschedulephp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/notifications/notifications-schedule.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesnotificationsnotificationssendphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/notifications/notifications-send.php</a></li>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/project/</li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesprojectprojectrepositoryphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/project/project-repository.php</a></li>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/attendee/</li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesroutesattendeelistphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/attendee/list.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventdeletephp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/delete.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventlisttrashedphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/list-trashed.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventtranslationsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/translations.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventtrashphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/trash.php</a></li>
<li>sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/translation/</li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludestranslationtranslationrepositoryphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/translation/translation-repository.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesurlsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/urls.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventtranslationsfooterphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event-translations-footer.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventtranslationsheaderphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event-translations-header.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventsattendeesphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-attendees.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventslisttrashedphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-list-trashed.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplatestranslationsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/translations.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsassetscsstranslationeventscss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/css/translation-events.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/css/translation-events.css    2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/css/translation-events.css      2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -6,7 +6,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> .translation-event-form #event-title,
</span><span class="cx" style="display: block; padding: 0 10px"> .translation-event-form #event-description {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        width: 30%;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ width: auto;
+       resize: both;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> .translation-event-form input[type="text"],
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -53,6 +54,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> .event-details-stats table {
</span><span class="cx" style="display: block; padding: 0 10px">        margin: 1rem;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> .event-details-stats table {
</span><span class="cx" style="display: block; padding: 0 10px">        width: 100%;
</span><span class="cx" style="display: block; padding: 0 10px">        table-layout: fixed;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -83,7 +85,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">        font-weight: bold;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.event-contributors li, .event-attendees li {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.event-contributors li,
+.event-attendees li {
</ins><span class="cx" style="display: block; padding: 0 10px">         display: inline-block;
</span><span class="cx" style="display: block; padding: 0 10px">        list-style-type: none;
</span><span class="cx" style="display: block; padding: 0 10px">        margin-right: 1em;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -91,7 +94,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">        width: 15em;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.event-contributors li .avatar, .event-attendees li .avatar {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.event-contributors li .avatar,
+.event-attendees li .avatar,
+td a.attendee-avatar .avatar {
</ins><span class="cx" style="display: block; padding: 0 10px">         border-radius: 50%;
</span><span class="cx" style="display: block; padding: 0 10px">        vertical-align: middle;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -106,6 +111,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        margin-top: 1em;
</span><span class="cx" style="display: block; padding: 0 10px">        font-size: smaller;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> .hide-event-url {
</span><span class="cx" style="display: block; padding: 0 10px">        display: none;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -246,7 +252,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">        padding: 0;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-input[type="submit"].remove-as-host, input[type="submit"].convert-to-host {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+input[type="submit"].remove-as-host,
+input[type="submit"].convert-to-host {
</ins><span class="cx" style="display: block; padding: 0 10px">         text-align: center;
</span><span class="cx" style="display: block; padding: 0 10px">        display: inline;
</span><span class="cx" style="display: block; padding: 0 10px">        margin-top: 1em;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -279,7 +286,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">        margin-bottom: 1em;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-.event-page-edit-link {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+.event-page-edit-link, .event-page-attendees-link {
</ins><span class="cx" style="display: block; padding: 0 10px">         float: right;
</span><span class="cx" style="display: block; padding: 0 10px">        text-decoration: none;
</span><span class="cx" style="display: block; padding: 0 10px">        justify-self: end;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -348,14 +355,82 @@
</span><span class="cx" style="display: block; padding: 0 10px">        border-bottom: var(--gp-color-btn-primary-bg) thin solid;
</span><span class="cx" style="display: block; padding: 0 10px">        text-decoration: none;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> ul.text-snippets {
</span><span class="cx" style="display: block; padding: 0 10px">        padding: 0;
</span><span class="cx" style="display: block; padding: 0 10px">        margin-left: 160px;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px"> .first-time-contributor-tada::after {
</span><span class="cx" style="display: block; padding: 0 10px">        content: ' ðŸŽ‰';
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ul#translation-links li {
+       margin-bottom: .5em;
+}
+
+.icons li .name {
+       display: none;
+}
+
+.icons li {
+       width: auto !important;
+}
+
+.event-attendees h2,
+.event-contributors h2 {
+       cursor: pointer;
+       display: inline-block;
+}
+
+ul.event-attendees-filter {
+       padding-left: 1rem;
+}
+
+ul.event-attendees-filter li {
+       display: inline;
+       margin-right: .4em;
+}
+
+form.translation-event-form {
+       float: left;
+       width: 85%;
+}
+
+div.event-edit-right {
+       float: right;
+       width: 15%;
+       text-align: right;
+}
+
+.view-event-page {
+       float: right;
+}
+
+a.active-filter {
+       font-weight: bold;
+}
+
+.event-edit-right a.manage-attendees-btn {
+       margin-top: 1em;
+}
+
+#quick-add.loading::after {
+       content: 'Loading...';
+}
+
+#quick-add summary {
+       cursor: pointer;
+}
+
+#quick-add {
+       padding: 0 .5em;
+       border-left: #d9d8d8 thin solid;
+       border-bottom: #d9d8d8 thin solid;
+       max-height: 8em;
+       overflow: auto;
+}
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /* show the event-details-right below instead of on the right on mobile */
</span><span class="cx" style="display: block; padding: 0 10px"> @media (max-width: 768px) {
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsassetsjstranslationeventsjs"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/js/translation-events.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/js/translation-events.js      2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/assets/js/translation-events.js        2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -21,11 +21,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        }
</span><span class="cx" style="display: block; padding: 0 10px">                                );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                $( '.delete-event' ).on(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $( '.trash-event' ).on(
</ins><span class="cx" style="display: block; padding: 0 10px">                                         'click',
</span><span class="cx" style="display: block; padding: 0 10px">                                        function ( e ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                                e.preventDefault();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                handleDelete()
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         handleTrash()
</ins><span class="cx" style="display: block; padding: 0 10px">                                         }
</span><span class="cx" style="display: block; padding: 0 10px">                                );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -38,6 +38,68 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                textArea.val( textAreaContent + $( this ).data( 'snippet' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                                        }
</span><span class="cx" style="display: block; padding: 0 10px">                                );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+                               $( '.event-attendees h2, .event-contributors h2' ).on(
+                                       'click',
+                                       function ( e ) {
+                                                       e.preventDefault();
+                                                       $( this ).closest( 'body' ).toggleClass( 'icons' );
+                                                       $( '.convert-to-host, .remove-as-host' ).toggle();
+                                       }
+                               );
+
+                               $( '#quick-add' ).on(
+                                       'toggle',
+                                       function () {
+                                               if ( $( this ).data( 'loaded' ) ) {
+                                                       return;
+                                               }
+                                               $( this ).addClass( 'loading' );
+                                               const options = {
+                                                       weekday: 'short',
+                                                       day: 'numeric',
+                                                       month: 'short',
+                                                       year: 'numeric'
+                                               };
+
+                                               fetch( 'https://central.wordcamp.org/wp-json/wp/v2/wordcamps?per_page=30&status=wcpt-scheduled' ).then(
+                                                       response => response.json()
+                                               ).then(
+                                                       function ( data ) {
+                                                               data.sort( ( a, b ) => a['Start Date (YYYY-mm-dd)'] - b['Start Date (YYYY-mm-dd)'] );
+                                                               const ul = $( '<ul>' );
+                                                               for ( const wordcamp of data ) {
+                                                                       const li = $( '<li>' ).data( 'wordcamp', wordcamp );
+                                                                       li.append( $( '<a>' ).attr( 'href', wordcamp.link ).text( wordcamp.title.rendered ) );
+                                                                       li.append( $( '<span>' ).text( ' ' + new Date( 1000 * wordcamp['Start Date (YYYY-mm-dd)'] ).toLocaleDateString( navigator.language, options ) + ' - ' + new Date( 1000 * wordcamp['End Date (YYYY-mm-dd)'] ).toLocaleDateString( navigator.language, options ) ) );
+                                                                       ul.append( li );
+                                                               }
+                                                               $( '#quick-add' ).data( 'loaded', true ).removeClass( 'loading' ).append( ul );
+                                                       }
+                                               );
+                                       }
+                               );
+                               $( document ).on(
+                                       'click',
+                                       '#quick-add a',
+                                       function ( e ) {
+                                               e.preventDefault();
+                                               e.stopPropagation();
+
+                                               const wordcamp = $( e.target ).closest( 'li' ).data( 'wordcamp' );
+                                               if ( ! wordcamp ) {
+                                                       return;
+                                               }
+
+                                               $( '#event-title' ).val( wordcamp.title.rendered );
+                                               $( '#event-description' ).val( wordcamp.content.rendered );
+                                               $( '#event-start' ).val( new Date( 1000 * wordcamp['Start Date (YYYY-mm-dd)'] ).toISOString().slice( 0,11 ) + '09:00' );
+                                               $( '#event-end' ).val( new Date( 1000 * wordcamp['End Date (YYYY-mm-dd)'] ).toISOString().slice( 0,11 ) + '18:00' );
+                                               $( '#event-timezone' ).val( wordcamp['Event Timezone'] );
+
+                                       }
+                               );
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -99,7 +161,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                        }
</span><span class="cx" style="display: block; padding: 0 10px">                                                        $( '#event-url' ).removeClass( 'hide-event-url' ).find( 'a' ).attr( 'href', response.data.eventUrl ).text( response.data.eventUrl );
</span><span class="cx" style="display: block; padding: 0 10px">                                                        if ( $is_creation ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                                $( '#delete-button' ).toggle();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                         $( '#trash-button' ).toggle();
</ins><span class="cx" style="display: block; padding: 0 10px">                                                         }
</span><span class="cx" style="display: block; padding: 0 10px">                                                        $gp.notices.success( response.data.message );
</span><span class="cx" style="display: block; padding: 0 10px">                                                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -113,13 +175,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                function handleDelete() {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         function handleTrash() {
</ins><span class="cx" style="display: block; padding: 0 10px">                         if ( ! confirm( 'Are you sure you want to delete this event?' ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                return;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                        const $form = $( '.translation-event-form' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $( '#form-name' ).val( 'delete_event' );
-                       $( '#event-form-action' ).val( 'delete' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $( '#form-name' ).val( 'trash_event' );
+                       $( '#event-form-action' ).val( 'trash' );
</ins><span class="cx" style="display: block; padding: 0 10px">                         $.ajax(
</span><span class="cx" style="display: block; padding: 0 10px">                                {
</span><span class="cx" style="display: block; padding: 0 10px">                                        type: 'POST',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -126,7 +188,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        url: $translation_event.url,
</span><span class="cx" style="display: block; padding: 0 10px">                                        data:$form.serialize(),
</span><span class="cx" style="display: block; padding: 0 10px">                                        success: function ( response ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                window.location = response.data.eventDeleteUrl;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         window.location = response.data.eventTrashUrl;
</ins><span class="cx" style="display: block; padding: 0 10px">                                         },
</span><span class="cx" style="display: block; padding: 0 10px">                                        error: function ( error ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                                $gp.notices.error( response.data.message );
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsautoloadphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/autoload.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/autoload.php 2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/autoload.php   2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,12 +1,18 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/upgrade.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require_once __DIR__ . '/includes/urls.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/templates/helper-functions.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/routes/route.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require_once __DIR__ . '/includes/routes/attendee/list.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/routes/event/create.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/routes/event/details.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/routes/event/edit.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require_once __DIR__ . '/includes/routes/event/trash.php';
+require_once __DIR__ . '/includes/routes/event/delete.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/routes/event/list.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require_once __DIR__ . '/includes/routes/event/list-trashed.php';
+require_once __DIR__ . '/includes/routes/event/translations.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/routes/user/attend-event.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/routes/user/host-event.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/routes/user/my-events.php';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -18,7 +24,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/event/event-repository.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/event/event-repository-cached.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/event/event-form-handler.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require_once __DIR__ . '/includes/notifications/notifications-schedule.php';
+require_once __DIR__ . '/includes/notifications/notifications-send.php';
+require_once __DIR__ . '/includes/event/event-capabilities.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/stats/stats-calculator.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/stats/stats-importer.php';
</span><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/stats/stats-listener.php';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+require_once __DIR__ . '/includes/project/project-repository.php';
+require_once __DIR__ . '/includes/translation/translation-repository.php';
</ins><span class="cx" style="display: block; padding: 0 10px"> require_once __DIR__ . '/includes/event-text-snippet.php';
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesattendeeattendeerepositoryphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee-repository.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee-repository.php    2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee-repository.php      2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3,7 +3,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> namespace Wporg\TranslationEvents\Attendee;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> use Exception;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use WP_User;
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> class Attendee_Repository {
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -82,8 +81,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $row = $wpdb->get_row(
</span><span class="cx" style="display: block; padding: 0 10px">                        $wpdb->prepare(
</span><span class="cx" style="display: block; padding: 0 10px">                                "
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                select *
-                               from {$gp_table_prefix}event_attendees
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         select
+                                       user_id,
+                                       is_host,
+                                       (
+                                               select group_concat( distinct locale )
+                                               from {$gp_table_prefix}event_actions
+                                               where event_id = attendees.event_id
+                                                 and user_id = attendees.user_id
+                                       ) as locales
+                               from {$gp_table_prefix}event_attendees attendees
</ins><span class="cx" style="display: block; padding: 0 10px">                                 where event_id = %d
</span><span class="cx" style="display: block; padding: 0 10px">                                  and user_id = %d
</span><span class="cx" style="display: block; padding: 0 10px">                        ",
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -99,31 +106,21 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        return null;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $attendee = new Attendee( $row->event_id, $row->user_id );
-               if ( '1' === $row->is_host ) {
-                       $attendee->mark_as_host();
-               }
-
-               return $attendee;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return new Attendee(
+                       $event_id,
+                       $row->user_id,
+                       '1' === $row->is_host,
+                       null === $row->locales ? array() : explode( ',', $row->locales ),
+               );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @return Attendee[] Attendees of the event.
-        */
-       public function get_attendees( int $event_id ): array { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
-               // TODO.
-               return array();
-       }
-
-       /**
-        * Get the hosts' users for an event.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Retrieve all the attendees of an event.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param int $event_id The id of the event.
-        *
-        * @return Attendee[] The hosts of the event.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @return Attendee[] Attendees of the event. Associative array with user_id as key.
</ins><span class="cx" style="display: block; padding: 0 10px">          * @throws Exception
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public function get_hosts( int $event_id ): array {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function get_attendees( int $event_id ): array {
</ins><span class="cx" style="display: block; padding: 0 10px">                 global $wpdb, $gp_table_prefix;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -132,27 +129,74 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $rows = $wpdb->get_results(
</span><span class="cx" style="display: block; padding: 0 10px">                        $wpdb->prepare(
</span><span class="cx" style="display: block; padding: 0 10px">                                "
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                select event_id, user_id
-                               from {$gp_table_prefix}event_attendees
-                               where event_id = %d and is_host = 1
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         select
+                                       user_id,
+                                       is_host,
+                                       (
+                                               select group_concat( distinct locale )
+                                               from {$gp_table_prefix}event_actions
+                                               where event_id = attendees.event_id
+                                                 and user_id = attendees.user_id
+                                       ) as locales
+                               from {$gp_table_prefix}event_attendees attendees
+                               where event_id = %d
</ins><span class="cx" style="display: block; padding: 0 10px">                         ",
</span><span class="cx" style="display: block; padding: 0 10px">                                array(
</span><span class="cx" style="display: block; padding: 0 10px">                                        $event_id,
</span><span class="cx" style="display: block; padding: 0 10px">                                )
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 ),
+                       OBJECT_K,
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px">                // phpcs:enable
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $hosts = array();
-               foreach ( $rows as $row ) {
-                       $host = new Attendee( $row->event_id, $row->user_id );
-                       $host->mark_as_host();
-                       $hosts[] = $host;
-               }
-               return $hosts;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         return array_map(
+                       function ( $row ) use ( $event_id ) {
+                               return new Attendee(
+                                       $event_id,
+                                       $row->user_id,
+                                       '1' === $row->is_host,
+                                       null === $row->locales ? array() : explode( ',', $row->locales ),
+                               );
+                       },
+                       $rows,
+               );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Get attendees without contributions for an event.
+        *
+        * @param int $event_id The id of the event.
+        *
+        * @return Attendee[] Associative array with user_id as key.
+        * @throws Exception
+        */
+       public function get_attendees_not_contributing( int $event_id ): array {
+               return array_filter(
+                       $this->get_attendees( $event_id ),
+                       function ( Attendee $attendee ) {
+                               return ! $attendee->is_contributor();
+                       }
+               );
+       }
+
+       /**
+        * Get the hosts' users for an event.
+        *
+        * @param int $event_id The id of the event.
+        *
+        * @return Attendee[] The hosts of the event. Associative array with user_id as key.
+        * @throws Exception
+        */
+       public function get_hosts( int $event_id ): array {
+               return array_filter(
+                       $this->get_attendees( $event_id ),
+                       function ( Attendee $attendee ) {
+                               return $attendee->is_host();
+                       }
+               );
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * @deprecated
</span><span class="cx" style="display: block; padding: 0 10px">         * TODO: This method should be moved out of this class because it's not about attendance,
</span><span class="cx" style="display: block; padding: 0 10px">         *       it returns events that match a condition (have a user as attendee), so it belongs in an event repository.
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesattendeeattendeephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee.php       2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/attendee/attendee.php 2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -10,9 +10,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">        private bool $is_host;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @var string[]
+        */
+       private array $contributed_locales;
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * @throws Exception
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public function __construct( int $event_id, int $user_id ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function __construct( int $event_id, int $user_id, bool $is_host = false, array $contributed_locales = array() ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( $event_id < 1 ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        throw new Exception( 'invalid event id' );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -20,9 +25,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        throw new Exception( 'invalid user id' );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->event_id = $event_id;
-               $this->user_id  = $user_id;
-               $this->is_host  = false;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->event_id            = $event_id;
+               $this->user_id             = $user_id;
+               $this->is_host             = $is_host;
+               $this->contributed_locales = $contributed_locales;
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        public function event_id(): int {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -37,6 +43,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                return $this->is_host;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        public function is_contributor(): bool {
+               return ! empty( $this->contributed_locales );
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         public function mark_as_host(): void {
</span><span class="cx" style="display: block; padding: 0 10px">                $this->is_host = true;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -44,4 +54,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public function mark_as_non_host(): void {
</span><span class="cx" style="display: block; padding: 0 10px">                $this->is_host = false;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        * @return string[]
+        */
+       public function contributed_locales(): array {
+               return $this->contributed_locales;
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventcapabilitiesphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-capabilities.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-capabilities.php                                (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-capabilities.php  2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,267 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace Wporg\TranslationEvents\Event;
+
+use Exception;
+use GP;
+use WP_User;
+use Wporg\TranslationEvents\Attendee\Attendee;
+use Wporg\TranslationEvents\Attendee\Attendee_Repository;
+use Wporg\TranslationEvents\Stats\Stats_Calculator;
+
+class Event_Capabilities {
+       private const MANAGE         = 'manage_translation_events';
+       private const CREATE         = 'create_translation_event';
+       private const VIEW           = 'view_translation_event';
+       private const EDIT           = 'edit_translation_event';
+       private const TRASH          = 'trash_translation_event';
+       private const DELETE         = 'delete_translation_event';
+       private const EDIT_ATTENDEES = 'edit_translation_event_attendees';
+
+       /**
+        * All the capabilities that concern Events.
+        */
+       private const CAPS = array(
+               self::MANAGE,
+               self::CREATE,
+               self::VIEW,
+               self::EDIT,
+               self::TRASH,
+               self::DELETE,
+               self::EDIT_ATTENDEES,
+       );
+
+       private Event_Repository_Interface $event_repository;
+       private Attendee_Repository $attendee_repository;
+       private Stats_Calculator $stats_calculator;
+
+       public function __construct(
+               Event_Repository_Interface $event_repository,
+               Attendee_Repository $attendee_repository,
+               Stats_Calculator $stats_calculator
+       ) {
+               $this->event_repository    = $event_repository;
+               $this->attendee_repository = $attendee_repository;
+               $this->stats_calculator    = $stats_calculator;
+       }
+
+       /**
+        * This function is automatically called whenever user_can() is called for one the capabilities in self::CAPS.
+        *
+        * @param string  $cap  Requested capability.
+        * @param array   $args Arguments that accompany the requested capability check.
+        * @param WP_User $user User for which we're evaluating the capability.
+        * @return bool
+        */
+       private function has_cap( string $cap, array $args, WP_User $user ): bool {
+               switch ( $cap ) {
+                       case self::MANAGE:
+                               return $this->has_manage( $user );
+                       case self::CREATE:
+                               return $this->has_create( $user );
+                       case self::VIEW:
+                       case self::EDIT:
+                       case self::TRASH:
+                       case self::DELETE:
+                       case self::EDIT_ATTENDEES:
+                               if ( ! isset( $args[2] ) || ! is_numeric( $args[2] ) ) {
+                                       return false;
+                               }
+                               $event = $this->event_repository->get_event( intval( $args[2] ) );
+                               if ( ! $event ) {
+                                       return false;
+                               }
+
+                               if ( self::VIEW === $cap ) {
+                                       return $this->has_view( $user, $event );
+                               }
+                               if ( self::EDIT === $cap ) {
+                                       return $this->has_edit( $user, $event );
+                               }
+                               if ( self::TRASH === $cap ) {
+                                       return $this->has_trash( $user, $event );
+                               }
+                               if ( self::DELETE === $cap ) {
+                                       return $this->has_delete( $user, $event );
+                               }
+                               if ( self::EDIT_ATTENDEES === $cap ) {
+                                       return $this->has_edit_attendees( $user, $event );
+                               }
+                               break;
+               }
+
+               return false;
+       }
+
+       /**
+        * Evaluate whether a user can manage events.
+        *
+        * @param WP_User $user User for which we're evaluating the capability.
+        * @return bool
+        */
+       private function has_manage( WP_User $user ): bool {
+               return $this->is_gp_admin( $user );
+       }
+
+       /**
+        * Evaluate whether a user can create events.
+        *
+        * @param WP_User $user User for which we're evaluating the capability.
+        * @return bool
+        */
+       private function has_create( WP_User $user ): bool {
+               return $this->is_gp_admin( $user );
+       }
+
+       /**
+        * Evaluate whether a user can view a specific event.
+        *
+        * @param WP_User $user  User for which we're evaluating the capability.
+        * @param Event   $event Event for which we're evaluating the capability.
+        * @return bool
+        */
+       private function has_view( WP_User $user, Event $event ): bool {
+               if ( $this->is_gp_admin( $user ) ) {
+                       return true;
+               }
+
+               return 'publish' === $event->status();
+       }
+
+       /**
+        * Evaluate whether a user can edit a specific event.
+        *
+        * @param WP_User $user  User for which we're evaluating the capability.
+        * @param Event   $event Event for which we're evaluating the capability.
+        * @return bool
+        */
+       private function has_edit( WP_User $user, Event $event ): bool {
+               if ( $event->end()->is_in_the_past() ) {
+                       return false;
+               }
+
+               if ( $this->stats_calculator->event_has_stats( $event->id() ) ) {
+                       return false;
+               }
+
+               if ( $event->author_id() === $user->ID ) {
+                       return true;
+               }
+
+               if ( user_can( $user->ID, 'edit_post', $event->id() ) ) {
+                       return true;
+               }
+
+               $attendee = $this->attendee_repository->get_attendee( $event->id(), $user->ID );
+               if ( ( $attendee instanceof Attendee ) && $attendee->is_host() ) {
+                       return true;
+               }
+
+               if ( $this->is_gp_admin( $user ) ) {
+                       return true;
+               }
+
+               return false;
+       }
+
+       /**
+        * Evaluate whether a user can trash a specific event.
+        *
+        * @param WP_User $user  User for which we're evaluating the capability.
+        * @param Event   $event Event for which we're evaluating the capability.
+        *
+        * @return bool
+        * @throws Exception
+        */
+       private function has_trash( WP_User $user, Event $event ): bool {
+               if ( $event->author_id() === $user->ID ) {
+                       return true;
+               }
+
+               $attendee = $this->attendee_repository->get_attendee( $event->id(), $user->ID );
+               if ( ( $attendee instanceof Attendee ) && $attendee->is_host() ) {
+                       return true;
+               }
+
+               if ( $this->is_gp_admin( $user ) ) {
+                       return true;
+               }
+
+               return false;
+       }
+
+       /**
+        * Evaluate whether a user can permanently delete a specific event.
+        *
+        * @param WP_User $user  User for which we're evaluating the capability.
+        * @param Event   $event Event for which we're evaluating the capability.
+        *
+        * @return bool
+        * @throws Exception
+        */
+       private function has_delete( WP_User $user, Event $event ): bool {
+               if ( ! $event->is_trashed() ) {
+                       // The event must be trashed first.
+                       return false;
+               }
+
+               if ( $this->is_gp_admin( $user ) ) {
+                       return true;
+               }
+
+               return false;
+       }
+
+       /**
+        * Evaluate whether a user can edit attendees for a specific event.
+        *
+        * @param WP_User $user  User for which we're evaluating the capability.
+        * @param Event   $event Event for which we're evaluating the capability.
+        * @return bool
+        */
+       private function has_edit_attendees( WP_User $user, Event $event ): bool {
+               if ( $this->is_gp_admin( $user ) ) {
+                       return true;
+               }
+
+               if ( $event->author_id() === $user->ID ) {
+                       return true;
+               }
+
+               $attendee = $this->attendee_repository->get_attendee( $event->id(), $user->ID );
+               if ( ( $attendee instanceof Attendee ) && $attendee->is_host() ) {
+                       return true;
+               }
+
+               return false;
+       }
+
+       /**
+        * Evaluate whether a user is a GlotPress admin.
+        *
+        * @param WP_User $user User for which we're evaluating the capability.
+        * @return bool
+        */
+       private function is_gp_admin( WP_User $user ): bool {
+               return apply_filters( 'gp_translation_events_can_crud_event', GP::$permission->user_can( $user, 'admin' ) );
+       }
+
+       public function register_hooks(): void {
+               add_action(
+                       'user_has_cap',
+                       function ( $allcaps, $caps, $args, $user ) {
+                               foreach ( $caps as $cap ) {
+                                       if ( ! in_array( $cap, self::CAPS, true ) ) {
+                                               continue;
+                                       }
+                                       if ( $this->has_cap( $cap, $args, $user ) ) {
+                                               $allcaps[ $cap ] = true;
+                                       }
+                               }
+                               return $allcaps;
+                       },
+                       10,
+                       4,
+               );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-capabilities.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventformhandlerphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-form-handler.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-form-handler.php        2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-form-handler.php  2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -5,19 +5,18 @@
</span><span class="cx" style="display: block; padding: 0 10px"> use DateTime;
</span><span class="cx" style="display: block; padding: 0 10px"> use DateTimeZone;
</span><span class="cx" style="display: block; padding: 0 10px"> use Exception;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use GP;
</del><span class="cx" style="display: block; padding: 0 10px"> use WP_Error;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use Wporg\TranslationEvents\Attendee\Attendee;
-use Wporg\TranslationEvents\Attendee\Attendee_Repository;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use Wporg\TranslationEvents\Notifications\Notifications_Schedule;
</ins><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Stats\Stats_Calculator;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use Wporg\TranslationEvents\Urls;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> class Event_Form_Handler {
</span><span class="cx" style="display: block; padding: 0 10px">        private Event_Repository_Interface $event_repository;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        private Attendee_Repository $attendee_repository;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private Notifications_Schedule $notifications_schedule;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public function __construct( Event_Repository_Interface $event_repository, Attendee_Repository $attendee_repository ) {
-               $this->event_repository    = $event_repository;
-               $this->attendee_repository = $attendee_repository;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function __construct( Event_Repository_Interface $event_repository ) {
+               $this->event_repository       = $event_repository;
+               $this->notifications_schedule = new Notifications_Schedule( $this->event_repository );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        public function handle( array $form_data ): void {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -24,42 +23,26 @@
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! is_user_logged_in() ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        wp_send_json_error( esc_html__( 'The user must be logged in.', 'gp-translation-events' ), 403 );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $action           = isset( $form_data['form_name'] ) ? sanitize_text_field( wp_unslash( $form_data['form_name'] ) ) : '';
-               $response_message = '';
-               $is_nonce_valid   = false;
-               $nonce_name       = '_event_nonce';
-               if ( ! in_array( $action, array( 'create_event', 'edit_event', 'delete_event' ), true ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $action = isset( $form_data['form_name'] ) ? sanitize_text_field( wp_unslash( $form_data['form_name'] ) ) : '';
+               if ( ! in_array( $action, array( 'create_event', 'edit_event', 'trash_event' ), true ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         wp_send_json_error( esc_html__( 'Invalid form name.', 'gp-translation-events' ), 403 );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                /**
-                * Filter the ability to create, edit, or delete an event.
-                *
-                * @param bool $can_crud_event Whether the user can create, edit, or delete an event.
-                */
-               $can_crud_event = apply_filters( 'gp_translation_events_can_crud_event', GP::$permission->current_user_can( 'admin' ) );
-               if ( 'create_event' === $action && ( ! $can_crud_event ) ) {
-                       wp_send_json_error( esc_html__( 'The user does not have permission to create an event.', 'gp-translation-events' ), 403 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $event_id = isset( $form_data['event_id'] ) ? sanitize_text_field( wp_unslash( $form_data['event_id'] ) ) : 0;
+
+               if ( 'create_event' === $action && ( ! current_user_can( 'create_translation_event' ) ) ) {
+                       wp_send_json_error( esc_html__( 'You do not have permissions to create events.', 'gp-translation-events' ), 403 );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( 'edit_event' === $action ) {
-                       $event_id = isset( $form_data['event_id'] ) ? sanitize_text_field( wp_unslash( $form_data['event_id'] ) ) : '';
-                       $event    = $this->event_repository->get_event( $event_id );
-                       $attendee = $this->attendee_repository->get_attendee( $event->id(), get_current_user_id() );
-                       if ( ! ( $can_crud_event || ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'edit_post', $event_id ) || $event->author_id() === get_current_user_id() ) ) {
-                               wp_send_json_error( esc_html__( 'The user does not have permission to edit or delete the event.', 'gp-translation-events' ), 403 );
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( 'edit_event' === $action && ( ! current_user_can( 'edit_translation_event', $event_id ) ) ) {
+                       wp_send_json_error( esc_html__( 'You do not have permissions to edit this event.', 'gp-translation-events' ), 403 );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( 'delete_event' === $action ) {
-                       $event_id         = isset( $form_data['event_id'] ) ? sanitize_text_field( wp_unslash( $form_data['event_id'] ) ) : '';
-                       $event            = $this->event_repository->get_event( $event_id );
-                       $attendee         = $this->attendee_repository->get_attendee( $event->id(), get_current_user_id() );
-                       $stats_calculator = new Stats_Calculator();
-                       if ( $stats_calculator->event_has_stats( $event->id() ) ) {
-                               wp_send_json_error( esc_html__( 'The event has stats so it cannot be deleted.', 'gp-translation-events' ), 422 );
-                       }
-                       if ( ! ( $can_crud_event || ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'delete_post', $event_id ) || get_current_user_id() === $event->author_id() ) ) {
-                               wp_send_json_error( esc_html__( 'You do not have permission to delete this event.', 'gp-translation-events' ), 403 );
-                       }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( 'trash_event' === $action && ( ! current_user_can( 'trash_translation_event', $event_id ) ) ) {
+                       wp_send_json_error( esc_html__( 'You do not have permissions to delete this event.', 'gp-translation-events' ), 403 );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $is_nonce_valid = false;
+               $nonce_name     = '_event_nonce';
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( isset( $form_data[ $nonce_name ] ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $nonce_value = sanitize_text_field( wp_unslash( $form_data[ $nonce_name ] ) );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( wp_verify_nonce( $nonce_value, $nonce_name ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -70,8 +53,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        wp_send_json_error( esc_html__( 'Nonce verification failed.', 'gp-translation-events' ), 403 );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( 'delete_event' === $action ) {
-                       // Delete event.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $response_message = '';
+               if ( 'trash_event' === $action ) {
+                       // Trash event.
</ins><span class="cx" style="display: block; padding: 0 10px">                         $event_id = intval( sanitize_text_field( wp_unslash( $form_data['event_id'] ) ) );
</span><span class="cx" style="display: block; padding: 0 10px">                        $event    = $this->event_repository->get_event( $event_id );
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( ! $event ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -88,12 +72,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                wp_send_json_error( esc_html__( 'Event has stats so it cannot be deleted.', 'gp-translation-events' ), 422 );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        if ( false === $this->event_repository->delete_event( $event ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 if ( false === $this->event_repository->trash_event( $event ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $response_message = esc_html__( 'Failed to delete event.', 'gp-translation-events' );
</span><span class="cx" style="display: block; padding: 0 10px">                                $event_status     = $event->status();
</span><span class="cx" style="display: block; padding: 0 10px">                        } else {
</span><span class="cx" style="display: block; padding: 0 10px">                                $response_message = esc_html__( 'Event deleted successfully.', 'gp-translation-events' );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                $event_status     = 'deleted';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $event_status     = 'trashed';
+                               $this->notifications_schedule->delete_scheduled_emails( $event_id );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                } else {
</span><span class="cx" style="display: block; padding: 0 10px">                        // Create or update event.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -134,6 +119,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        return;
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="cx" style="display: block; padding: 0 10px">                                $response_message = esc_html__( 'Event created successfully.', 'gp-translation-events' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                $this->notifications_schedule->schedule_emails( $result );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( 'edit_event' === $action ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $event = $this->event_repository->get_event( $new_event->id() );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -158,6 +144,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        return;
</span><span class="cx" style="display: block; padding: 0 10px">                                }
</span><span class="cx" style="display: block; padding: 0 10px">                                $response_message = esc_html__( 'Event updated successfully', 'gp-translation-events' );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                $this->notifications_schedule->schedule_emails( $result );
</ins><span class="cx" style="display: block; padding: 0 10px">                         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $event_id     = $new_event->id();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -164,16 +151,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $event_status = $new_event->status();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                list( $permalink, $post_name ) = get_sample_permalink( $event_id );
-               $permalink                     = str_replace( '%pagename%', $post_name, $permalink );
</del><span class="cx" style="display: block; padding: 0 10px">                 wp_send_json_success(
</span><span class="cx" style="display: block; padding: 0 10px">                        array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                'message'        => $response_message,
-                               'eventId'        => $event_id,
-                               'eventUrl'       => str_replace( '%pagename%', $post_name, $permalink ),
-                               'eventStatus'    => $event_status,
-                               'eventEditUrl'   => esc_url( gp_url( '/events/edit/' . $event_id ) ),
-                               'eventDeleteUrl' => esc_url( gp_url( '/events/my-events/' ) ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         'message'       => $response_message,
+                               'eventId'       => $event_id,
+                               'eventStatus'   => $event_status,
+                               'eventUrl'      => Urls::event_details_absolute( $event_id ),
+                               'eventEditUrl'  => Urls::event_edit( $event_id ),
+                               'eventTrashUrl' => Urls::my_events(), // The URL the user is redirected to after trashing.
</ins><span class="cx" style="display: block; padding: 0 10px">                         )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -200,7 +185,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $event_timezone = isset( $data['event_timezone'] ) ? sanitize_text_field( wp_unslash( $data['event_timezone'] ) ) : '';
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $event_status = '';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( isset( $data['event_form_action'] ) && in_array( $data['event_form_action'], array( 'draft', 'publish', 'delete' ), true ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( isset( $data['event_form_action'] ) && in_array( $data['event_form_action'], array( 'draft', 'publish', 'trash' ), true ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         $event_status = sanitize_text_field( wp_unslash( $data['event_form_action'] ) );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventrepositorycachedphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-cached.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-cached.php   2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-cached.php     2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -31,6 +31,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                return $event_id_or_error;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        public function trash_event( Event $event ) {
+               parent::trash_event( $event );
+               $this->invalidate_cache();
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         public function delete_event( Event $event ) {
</span><span class="cx" style="display: block; padding: 0 10px">                parent::delete_event( $event );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->invalidate_cache();
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventrepositoryinterfacephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-interface.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-interface.php        2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository-interface.php  2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -25,10 +25,19 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public function update_event( Event $event );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Delete an Event.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * Trash an Event.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * @param Event $event Event to delete.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+  * @param Event $event Event to trash.
</ins><span class="cx" style="display: block; padding: 0 10px">          *
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * @return Event|false Trashed event or false on error.
+        */
+       public function trash_event( Event $event );
+
+       /**
+        * Permanently delete an Event.
+        *
+        * @param Event $event Event to permanently delete.
+        *
</ins><span class="cx" style="display: block; padding: 0 10px">          * @return Event|false Deleted event or false on error.
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function delete_event( Event $event );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -80,6 +89,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public function get_past_events( int $page = -1, int $page_size = -1 ): Events_Query_Result;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         * Get events that are trashed.
+        *
+        * @param int $page      Index of the page to return.
+        * @param int $page_size Page size.
+        *
+        * @return Events_Query_Result
+        * @throws Exception
+        */
+       public function get_trashed_events( int $page = -1, int $page_size = -1 ): Events_Query_Result;
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Get events that are currently active for a given user.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param int $user_id   Id of the user.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -89,6 +109,18 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @return Events_Query_Result
</span><span class="cx" style="display: block; padding: 0 10px">         * @throws Exception
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        public function get_current_events_for_user( int $user_id, int $page = -1, int $page_size = -1 ): Events_Query_Result;
+
+       /**
+        * Get events that are currently active or happening in the future, for a given user.
+        *
+        * @param int $user_id   Id of the user.
+        * @param int $page      Index of the page to return.
+        * @param int $page_size Page size.
+        *
+        * @return Events_Query_Result
+        * @throws Exception
+        */
</ins><span class="cx" style="display: block; padding: 0 10px">         public function get_current_and_upcoming_events_for_user( int $user_id, int $page = -1, int $page_size = -1 ): Events_Query_Result;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventrepositoryphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository.php  2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event-repository.php    2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -20,7 +20,33 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->attendee_repository = $attendee_repository;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        /**
+        * Get or create the parent post for the year.
+        *
+        * @return int|WP_Error
+        */
+       private function get_year_post_id( string $year ) {
+               $year_post = get_page_by_path( $year, OBJECT, self::POST_TYPE );
+               if ( ! $year_post ) {
+                       return wp_insert_post(
+                               array(
+                                       'post_type'    => self::POST_TYPE,
+                                       'post_title'   => $year,
+                                       'post_name'    => $year,
+                                       'post_status'  => 'publish',
+                                       'post_content' => '',
+                               )
+                       );
+               }
+               return $year_post->ID;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         public function insert_event( Event $event ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $post_parent = $this->get_year_post_id( $event->start()->utc()->format( 'Y' ) );
+               if ( is_wp_error( $post_parent ) ) {
+                       return $post_parent;
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $event_id_or_error = wp_insert_post(
</span><span class="cx" style="display: block; padding: 0 10px">                        array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'post_type'    => self::POST_TYPE,
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -28,6 +54,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'post_title'   => $event->title(),
</span><span class="cx" style="display: block; padding: 0 10px">                                'post_content' => $event->description(),
</span><span class="cx" style="display: block; padding: 0 10px">                                'post_status'  => $event->status(),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                'post_parent'  => $post_parent,
</ins><span class="cx" style="display: block; padding: 0 10px">                         )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $event_id_or_error instanceof WP_Error ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -40,6 +67,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        public function update_event( Event $event ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $post_parent = $this->get_year_post_id( $event->start()->utc()->format( 'Y' ) );
+               if ( is_wp_error( $post_parent ) ) {
+                       return $post_parent;
+               }
</ins><span class="cx" style="display: block; padding: 0 10px">                 $event_id_or_error = wp_update_post(
</span><span class="cx" style="display: block; padding: 0 10px">                        array(
</span><span class="cx" style="display: block; padding: 0 10px">                                'ID'           => $event->id(),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -47,6 +78,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                'post_title'   => $event->title(),
</span><span class="cx" style="display: block; padding: 0 10px">                                'post_content' => $event->description(),
</span><span class="cx" style="display: block; padding: 0 10px">                                'post_status'  => $event->status(),
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                'post_parent'  => $post_parent,
</ins><span class="cx" style="display: block; padding: 0 10px">                         )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $event_id_or_error instanceof WP_Error ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -57,7 +89,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                return $event->id();
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public function delete_event( Event $event ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function trash_event( Event $event ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $result = wp_trash_post( $event->id() );
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! $result ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        return false;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -65,6 +97,32 @@
</span><span class="cx" style="display: block; padding: 0 10px">                return $event;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        public function delete_event( Event $event ) {
+               $result = wp_delete_post( $event->id(), true );
+               if ( ! $result ) {
+                       return false;
+               }
+
+               // Delete attendees.
+               $attendees = $this->attendee_repository->get_attendees( $event->id() );
+               foreach ( $attendees as $attendee ) {
+                       $this->attendee_repository->remove_attendee( $event->id(), $attendee->user_id() );
+               }
+
+               // Delete stats.
+               global $wpdb, $gp_table_prefix;
+               // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
+               // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
+               $wpdb->delete(
+                       "{$gp_table_prefix}event_actions",
+                       array( 'event_id' => $event->id() ),
+                       array( '%d' ),
+               );
+               // phpcs:enable
+
+               return $event;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         public function get_event( int $id ): ?Event {
</span><span class="cx" style="display: block; padding: 0 10px">                $post = $this->get_event_post( $id );
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! $post ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -152,6 +210,48 @@
</span><span class="cx" style="display: block; padding: 0 10px">                // phpcs:enable
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        public function get_trashed_events( int $page = - 1, int $page_size = - 1 ): Events_Query_Result {
+               return $this->execute_events_query(
+                       $page,
+                       $page_size,
+                       array(
+                               'post_status' => 'trash',
+                       )
+               );
+       }
+
+       public function get_current_events_for_user( int $user_id, int $page = -1, int $page_size = -1 ): Events_Query_Result {
+               $now = new DateTimeImmutable( 'now', new DateTimeZone( 'UTC' ) );
+
+               // phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_query
+               // phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_key
+               return $this->execute_events_query(
+                       $page,
+                       $page_size,
+                       array(
+                               'meta_query' => array(
+                                       array(
+                                               'key'     => '_event_start',
+                                               'value'   => $now->format( 'Y-m-d H:i:s' ),
+                                               'compare' => '<=',
+                                               'type'    => 'DATETIME',
+                                       ),
+                                       array(
+                                               'key'     => '_event_end',
+                                               'value'   => $now->format( 'Y-m-d H:i:s' ),
+                                               'compare' => '>=',
+                                               'type'    => 'DATETIME',
+                                       ),
+                               ),
+                               'meta_key'   => '_event_start',
+                               'orderby'    => 'meta_value',
+                               'order'      => 'ASC',
+                       ),
+                       $this->attendee_repository->get_events_for_user( $user_id ),
+               );
+               // phpcs:enable
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         public function get_current_and_upcoming_events_for_user( int $user_id, int $page = -1, int $page_size = -1 ): Events_Query_Result {
</span><span class="cx" style="display: block; padding: 0 10px">                $now = new DateTimeImmutable( 'now', new DateTimeZone( 'UTC' ) );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -413,8 +513,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        private function update_event_meta( Event $event ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $hosts     = $this->attendee_repository->get_hosts( $event->id() );
+               $hosts_ids = array_map(
+                       function ( $host ) {
+                               return $host->user_id();
+                       },
+                       $hosts
+               );
+               $hosts_ids = implode( ', ', $hosts_ids );
</ins><span class="cx" style="display: block; padding: 0 10px">                 update_post_meta( $event->id(), '_event_start', $event->start()->utc()->format( 'Y-m-d H:i:s' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                update_post_meta( $event->id(), '_event_end', $event->end()->utc()->format( 'Y-m-d H:i:s' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                update_post_meta( $event->id(), '_event_timezone', $event->timezone()->getName() );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                update_post_meta( $event->id(), '_hosts', $hosts_ids );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludeseventeventphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event.php     2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/event/event.php       2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -87,6 +87,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                return $this->end;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        public function is_trashed(): bool {
+               return 'trash' === $this->status;
+       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         public function is_active(): bool {
</span><span class="cx" style="display: block; padding: 0 10px">                $now = new DateTimeImmutable( 'now', new DateTimeZone( 'UTC' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                return $now >= $this->start->utc() && $now < $this->end->utc();
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -141,7 +145,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @throws InvalidStatus
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function set_status( string $status ): void {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( ! in_array( $status, array( 'draft', 'publish' ), true ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( ! in_array( $status, array( 'draft', 'publish', 'trash' ), true ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         throw new InvalidStatus();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px">                $this->status = $status;
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesnotificationsnotificationsschedulephp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/notifications/notifications-schedule.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/notifications/notifications-schedule.php                            (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/notifications/notifications-schedule.php      2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,72 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace Wporg\TranslationEvents\Notifications;
+
+use Wporg\TranslationEvents\Event\Event_Repository_Interface;
+
+class Notifications_Schedule {
+       private Event_Repository_Interface $event_repository;
+
+       /**
+        * Notifications_Schedule constructor.
+        *
+        * @param Event_Repository_Interface $event_repository    Event repository.
+        */
+       public function __construct( Event_Repository_Interface $event_repository ) {
+               $this->event_repository = $event_repository;
+       }
+
+       /**
+        * Schedule emails for events.
+        *
+        * @param int $post_id Post ID.
+        *
+        * @return void
+        */
+       public function schedule_emails( int $post_id ) {
+               $event = $this->event_repository->get_event( $post_id );
+               if ( ! $event ) {
+                       return;
+               }
+
+               $this->delete_scheduled_emails( $post_id );
+               if ( 'publish' === get_post_status( $post_id ) ) {
+                       $args                  = array(
+                               'post_id' => $post_id,
+                       );
+                       $now                   = time();
+                       $new_next_1h_schedule  = $event->start()->getTimestamp() - HOUR_IN_SECONDS;
+                       $new_next_24h_schedule = $event->start()->getTimestamp() - 24 * HOUR_IN_SECONDS;
+                       if ( $new_next_1h_schedule > $now ) {
+                               wp_schedule_single_event( $new_next_1h_schedule, 'wporg_gp_translation_events_email_notifications_1h', $args );
+                       }
+                       if ( $new_next_24h_schedule > $now ) {
+                               wp_schedule_single_event( $new_next_24h_schedule, 'wporg_gp_translation_events_email_notifications_24h', $args );
+                       }
+               }
+       }
+
+       /**
+        * Delete scheduled emails for events.
+        *
+        * @param int $post_id Post ID.
+        *
+        * @return void
+        */
+       public function delete_scheduled_emails( int $post_id ): void {
+               $args = array(
+                       'post_id' => $post_id,
+               );
+
+               $unscheduled_1h    = false;
+               $unscheduled_24h   = false;
+               $next_1h_schedule  = wp_next_scheduled( 'wporg_gp_translation_events_email_notifications_1h', $args );
+               $next_24h_schedule = wp_next_scheduled( 'wporg_gp_translation_events_email_notifications_24h', $args );
+               if ( $next_1h_schedule ) {
+                       $unscheduled_1h = wp_unschedule_event( $next_1h_schedule, 'wporg_gp_translation_events_email_notifications_1h', $args );
+               }
+               if ( $next_24h_schedule ) {
+                       $unscheduled_24h = wp_unschedule_event( $next_24h_schedule, 'wporg_gp_translation_events_email_notifications_24h', $args );
+               }
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/notifications/notifications-schedule.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesnotificationsnotificationssendphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/notifications/notifications-send.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/notifications/notifications-send.php                                (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/notifications/notifications-send.php  2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,177 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace Wporg\TranslationEvents\Notifications;
+
+use DateTime;
+use DateTimeZone;
+use Wporg\TranslationEvents\Attendee\Attendee;
+use Wporg\TranslationEvents\Attendee\Attendee_Repository;
+use Wporg\TranslationEvents\Event\Event;
+use Wporg\TranslationEvents\Event\Event_Repository_Interface;
+use WP_User;
+use Wporg\TranslationEvents\Event\Event_Start_Date;
+
+class Notifications_Send {
+
+       private Attendee_Repository $attendee_repository;
+       private Event_Repository_Interface $event_repository;
+
+       /**
+        * Notifications_Send constructor.
+        *
+        * @param Event_Repository_Interface $event_repository    Event repository.
+        * @param Attendee_Repository        $attendee_repository Attendee repository.
+        */
+       public function __construct(
+               Event_Repository_Interface $event_repository,
+               Attendee_Repository $attendee_repository
+       ) {
+               $this->event_repository    = $event_repository;
+               $this->attendee_repository = $attendee_repository;
+               add_action( 'wporg_gp_translation_events_email_notifications_1h', array( $this, 'send_notifications' ), 10, 1 );
+               add_action( 'wporg_gp_translation_events_email_notifications_24h', array( $this, 'send_notifications' ), 10, 1 );
+       }
+
+       /**
+        * Send notifications to the attendees of the event.
+        *
+        * @param int $post_id Post ID.
+        *
+        * @return void
+        * @throws \Exception
+        */
+       public function send_notifications( int $post_id ) {
+               $event = $this->event_repository->get_event( $post_id );
+               if ( null === $event ) {
+                       return;
+               }
+               $attendees = $this->attendee_repository->get_attendees( $event->id() );
+               foreach ( $attendees as $attendee ) {
+                       $this->send_email_notification( $event, $attendee );
+               }
+       }
+
+       /**
+        * Send an email notification to the attendee of the event.
+        *
+        * @param Event    $event        The event.
+        * @param Attendee $attendee     The attendee.
+        *
+        * @return void
+        */
+       public function send_email_notification( Event $event, Attendee $attendee ): void {
+               $user    = get_user_by( 'ID', $attendee->user_id() );
+               $subject = $this->get_email_subject( $event );
+               $message = $this->get_email_message( $user, $event );
+               wp_mail(
+                       $user->user_email,
+                       $subject,
+                       $message,
+                       'Content-Type: text/html'
+               );
+       }
+
+       /**
+        * Get the email subject.
+        *
+        * @param Event $event The event.
+        *
+        * @return string
+        */
+       private function get_email_subject( Event $event ): string {
+               $subject = sprintf(
+               // translators: %s: Event title.
+                       esc_html__( 'Translation Event Coming Up: %s', 'gp-translation-events' ),
+                       esc_html( $event->title() )
+               );
+
+               return $subject;
+       }
+
+       /**
+        * Get the email message.
+        *
+        * @param WP_User $user  The user.
+        * @param Event   $event The event.
+        *
+        * @return string
+        */
+       private function get_email_message( WP_User $user, Event $event ): string {
+               $start_date = $event->start();
+               // translators: %s: User display name.
+               $message  = sprintf( esc_html__( 'Hi %s,', 'gp-translation-events' ), $user->display_name );
+               $message .= '<br><br>';
+               $message .= esc_html(
+                       sprintf(
+                       // translators: %s is the event title.
+                               __( 'We are sending you this e-mail because you have signed up for the translation event "%1$s".', 'gp-translation-events' ),
+                               $event->title()
+                       )
+               );
+               $message .= ' ';
+               $message .= esc_html(
+                       sprintf(
+                       // translators: %s: Time until event starts.
+                               __( 'The event will start in %s.', 'gp-translation-events' ),
+                               $this->calculate_time_until_event( $event->start() )
+                       )
+               );
+               $message         .= ' ';
+               $message         .= esc_html__( "We're looking forward to translating with you!", 'gp-translation-events' );
+               $message         .= '<br>';
+               $local_start_date = $event->start()->setTimezone( new DateTimeZone( $event->timezone()->getName() ) );
+               $message         .= sprintf(
+                       // translators: %1$s: Event start date in 'Y-m-d H:i' format. %2$s: Event timezone name.
+                       esc_html__( 'The event will start at %1$s (local %2$s time).', 'gp-translation-events' ),
+                       $local_start_date->format( 'Y-m-d H:i' ),
+                       $local_start_date->getTimezone()->getName()
+               );
+               $message .= '<br><br>';
+               $message .= wp_kses(
+                       sprintf(
+                               // translators: %1$s: Event permalink.
+                               __( 'You can get more information about the event or stop attending go to <a href="%1$s">%1$s</a>.', 'gp-translation-events' ),
+                               esc_url( home_url( gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) ) ) )
+                       ),
+                       array( 'a' => array( 'href' => array() ) )
+               );
+               $message .= '<br><br>';
+               $message .= esc_html__( 'Have a nice day', 'gp-translation-events' );
+               $message .= '<br><br>';
+               $message .= esc_html__( 'The Global Polyglots Team', 'gp-translation-events' );
+               $message .= '<br>';
+
+               return $message;
+       }
+
+       /**
+        * Calculate the time until the event starts.
+        *
+        * @param Event_Start_Date $start_date The start date of the event.
+        *
+        * @return string
+        */
+       private function calculate_time_until_event( Event_Start_Date $start_date ): string {
+               $now              = new DateTime( 'now', new DateTimeZone( 'UTC' ) );
+               $diff             = $start_date->diff( $now );
+               $days_to_start    = $diff->days;
+               $hours_to_start   = $diff->h;
+               $minutes_to_start = $diff->i;
+               $message          = '';
+               if ( $days_to_start >= 1 ) {
+                       // translators: %d: Number of days.
+                       $message .= sprintf( _n( '%d day', '%d days', $days_to_start, 'gp-translation-events' ), $days_to_start );
+               } elseif ( $hours_to_start > 1 ) {
+                       // translators: %d: Number of hours.
+                       $message .= sprintf( esc_html__( '%d hours', 'gp-translation-events' ), $hours_to_start );
+               } elseif ( 1 === $hours_to_start ) {
+                       // translators: %d: Number of minutes.
+                       $message .= sprintf( _n( '1 hour and %d minute', '1 hour and %d minutes', $minutes_to_start, 'gp-translation-events' ), $minutes_to_start );
+               } else {
+                       // translators: %d: Number of minutes.
+                       $message .= sprintf( _n( '%d minute', '%d minutes', $minutes_to_start, 'gp-translation-events' ), $minutes_to_start );
+               }
+
+               return $message;
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/notifications/notifications-send.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesprojectprojectrepositoryphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/project/project-repository.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/project/project-repository.php                              (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/project/project-repository.php        2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,56 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace Wporg\TranslationEvents\Project;
+
+use GP;
+
+class Project_Repository {
+       /**
+        * Get projects for an event.
+        */
+       public function get_for_event( int $event_id ): array {
+               global $wpdb, $gp_table_prefix;
+
+               // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+               // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
+               // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
+               // phpcs thinks we're doing a schema change but we aren't.
+               // phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange
+               $rows = $wpdb->get_results(
+                       $wpdb->prepare(
+                               "
+                               select
+                                       o.project_id as project,
+                                       group_concat( distinct e.locale ) as locales,
+                                       sum(action = 'create') as created,
+                                       count(*) as total,
+                                       count(distinct user_id) as users
+                               from {$gp_table_prefix}event_actions e, {$gp_table_prefix}originals o
+                               where e.event_id = %d and e.original_id = o.id
+                               group by o.project_id
+                       ",
+                               array(
+                                       $event_id,
+                               )
+                       )
+               );
+               // phpcs:enable
+
+               $projects = array();
+               foreach ( $rows as $row ) {
+                       $row->project      = GP::$project->get( $row->project );
+                       $project_name      = $row->project->name;
+                       $parent_project_id = $row->project->parent_project_id;
+                       while ( $parent_project_id ) {
+                               $parent_project    = GP::$project->get( $parent_project_id );
+                               $parent_project_id = $parent_project->parent_project_id;
+                               $project_name      = substr( htmlspecialchars_decode( $parent_project->name ), 0, 35 ) . ' - ' . $project_name;
+                       }
+                       $projects[ $project_name ] = $row;
+               }
+
+               ksort( $projects );
+
+               return $projects;
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/project/project-repository.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesroutesattendeelistphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/attendee/list.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/attendee/list.php                            (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/attendee/list.php      2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,57 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace Wporg\TranslationEvents\Routes\Attendee;
+
+use Wporg\TranslationEvents\Attendee\Attendee_Repository;
+use Wporg\TranslationEvents\Routes\Route;
+use Wporg\TranslationEvents\Translation_Events;
+use Wporg\TranslationEvents\Event\Event_Repository_Interface;
+
+
+/**
+ * Displays the attendees list page.
+ */
+class List_Route extends Route {
+       private Attendee_Repository $attendee_repository;
+       private Event_Repository_Interface $event_repository;
+
+
+
+       public function __construct() {
+               parent::__construct();
+               $this->attendee_repository = new Attendee_Repository();
+               $this->event_repository    = Translation_Events::get_event_repository();
+       }
+
+       public function handle( string $event_slug ): void {
+               global $wp;
+               $user             = wp_get_current_user();
+               $is_active_filter = false;
+               $hide_sub_head    = true;
+               if ( ! is_user_logged_in() ) {
+                       wp_safe_redirect( wp_login_url( home_url( $wp->request ) ) );
+                       exit;
+               }
+
+               $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();
+               }
+               if ( ! current_user_can( 'edit_translation_event_attendees', $event->id() ) ) {
+                       $this->die_with_error( esc_html__( 'You do not have permission to edit this event\'s attendees.', 'gp-translation-events' ), 403 );
+               }
+               $attendees = array();
+               if ( gp_get( 'filter' ) && 'hosts' === gp_get( 'filter' ) ) {
+                       $is_active_filter = true;
+                       $attendees        = $this->attendee_repository->get_hosts( $event->id() );
+               } else {
+                       $attendees = $this->attendee_repository->get_attendees( $event->id() );
+               }
+
+               $this->tmpl( 'events-attendees', get_defined_vars() );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/attendee/list.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventcreatephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/create.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/create.php     2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/create.php       2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -16,19 +16,25 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        wp_safe_redirect( wp_login_url( home_url( $wp->request ) ) );
</span><span class="cx" style="display: block; padding: 0 10px">                        exit;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $event_page_title         = 'Create Event';
-               $event_form_name          = 'create_event';
-               $css_show_url             = 'hide-event-url';
-               $event_id                 = null;
-               $event_title              = '';
-               $event_description        = '';
-               $event_url                = '';
-               $create_delete_button     = true;
-               $visibility_delete_button = 'none';
-               $event_timezone           = null;
-               $event_start              = new Event_Start_Date( date_i18n( 'Y - m - d H:i' ) );
-               $event_end                = new Event_End_Date( date_i18n( 'Y - m - d H:i' ) );
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                if ( ! current_user_can( 'create_translation_event' ) ) {
+                       $this->die_with_error( 'You do not have permission to create events.' );
+               }
+
+               $event_page_title        = 'Create Event';
+               $event_form_name         = 'create_event';
+               $css_show_url            = 'hide-event-url';
+               $event_id                = null;
+               $event_title             = '';
+               $event_description       = '';
+               $event_url               = '';
+               $create_trash_button     = true;
+               $visibility_trash_button = 'none';
+               $event_timezone          = null;
+               $event_start             = new Event_Start_Date( date_i18n( 'Y - m - d H:i' ) );
+               $event_end               = new Event_End_Date( date_i18n( 'Y - m - d H:i' ) );
+               $event_slug              = '';
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 $this->tmpl( 'events-form', get_defined_vars() );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventdeletephp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/delete.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/delete.php                             (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/delete.php       2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,49 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace Wporg\TranslationEvents\Routes\Event;
+
+use Wporg\TranslationEvents\Attendee\Attendee;
+use Wporg\TranslationEvents\Attendee\Attendee_Repository;
+use Wporg\TranslationEvents\Event\Event_Repository_Interface;
+use Wporg\TranslationEvents\Routes\Route;
+use Wporg\TranslationEvents\Stats\Stats_Importer;
+use Wporg\TranslationEvents\Translation_Events;
+use Wporg\TranslationEvents\Urls;
+
+/**
+ * Permanently delete an Event.
+ */
+class Delete_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( int $event_id ): void {
+               if ( ! is_user_logged_in() ) {
+                       global $wp;
+                       wp_safe_redirect( wp_login_url( home_url( $wp->request ) ) );
+                       exit;
+               }
+
+               $event = $this->event_repository->get_event( $event_id );
+               if ( ! $event ) {
+                       $this->die_with_404();
+               }
+
+               if ( ! current_user_can( 'manage_translation_events', $event->id() ) ) {
+                       $this->die_with_error( esc_html__( 'You do not have permission to delete events.', 'gp-translation-events' ), 403 );
+               }
+
+               if ( ! current_user_can( 'delete_translation_event', $event->id() ) ) {
+                       $this->die_with_error( esc_html__( 'You do not have permission to delete this event.', 'gp-translation-events' ), 403 );
+               }
+
+               $this->event_repository->delete_event( $event );
+
+               wp_safe_redirect( Urls::events_home() );
+               exit;
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/delete.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventdetailsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/details.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/details.php    2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/details.php      2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3,12 +3,13 @@
</span><span class="cx" style="display: block; padding: 0 10px"> namespace Wporg\TranslationEvents\Routes\Event;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> use Exception;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use GP;
</del><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Attendee\Attendee;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Attendee\Attendee_Repository;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Event\Event_Repository_Interface;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use Wporg\TranslationEvents\Project\Project_Repository;
</ins><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Routes\Route;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Stats\Stats_Calculator;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use Wporg\TranslationEvents\Translation\Translation_Repository;
</ins><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Translation_Events;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -17,11 +18,17 @@
</span><span class="cx" style="display: block; padding: 0 10px"> class Details_Route extends Route {
</span><span class="cx" style="display: block; padding: 0 10px">        private Event_Repository_Interface $event_repository;
</span><span class="cx" style="display: block; padding: 0 10px">        private Attendee_Repository $attendee_repository;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        private Translation_Repository $translation_repository;
+       private Project_Repository $project_repository;
+       private Stats_Calculator $stats_calculator;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        public function __construct() {
</span><span class="cx" style="display: block; padding: 0 10px">                parent::__construct();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->event_repository    = Translation_Events::get_event_repository();
-               $this->attendee_repository = Translation_Events::get_attendee_repository();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->event_repository       = Translation_Events::get_event_repository();
+               $this->attendee_repository    = Translation_Events::get_attendee_repository();
+               $this->translation_repository = new Translation_Repository();
+               $this->project_repository     = new Project_Repository();
+               $this->stats_calculator       = new Stats_Calculator();
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        public function handle( string $event_slug ): void {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -35,13 +42,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->die_with_404();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                /**
-                * Filter the ability to create, edit, or delete an event.
-                *
-                * @param bool $can_crud_event Whether the user can create, edit, or delete an event.
-                */
-               $can_crud_event = apply_filters( 'gp_translation_events_can_crud_event', GP::$permission->current_user_can( 'admin' ) );
-               if ( 'publish' !== $event->status() && ! $can_crud_event ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( ! current_user_can( 'view_translation_event', $event->id() ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                         $this->die_with_error( esc_html__( 'You are not authorized to view this page.', 'gp-translation-events' ), 403 );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -51,17 +52,50 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $event_start       = $event->start();
</span><span class="cx" style="display: block; padding: 0 10px">                $event_end         = $event->end();
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $attendee          = $this->attendee_repository->get_attendee( $event->id(), $user->ID );
-               $user_is_attending = $attendee instanceof Attendee;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $projects              = $this->project_repository->get_for_event( $event->id() );
+               $attendees             = $this->attendee_repository->get_attendees( $event->id() );
+               $current_user_attendee = $attendees[ $user->ID ] ?? null;
+               $user_is_attending     = $current_user_attendee instanceof Attendee;
+               $user_is_contributor   = $user_is_attending && $current_user_attendee->is_contributor();
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $stats_calculator = new Stats_Calculator();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $hosts = array_filter(
+                       $attendees,
+                       function ( Attendee $attendee ) {
+                               return $attendee->is_host();
+                       }
+               );
+
+               $contributors = array_filter(
+                       $attendees,
+                       function ( Attendee $attendee ) {
+                               return $attendee->is_contributor();
+                       }
+               );
+
+               $attendees_not_contributing = array_filter(
+                       $attendees,
+                       function ( Attendee $attendee ) {
+                               return ! $attendee->is_contributor();
+                       }
+               );
+
+               $contributor_ids = array_map(
+                       function ( Attendee $contributor ) {
+                               return $contributor->user_id();
+                       },
+                       $contributors
+               );
+
+               $new_contributor_ids = array();
+               $translations_counts = $this->translation_repository->count_translations_before( $contributor_ids, $event->start() );
+               foreach ( $translations_counts as $user_id => $count ) {
+                       if ( $count <= 10 ) {
+                               $new_contributor_ids[ $user_id ] = true;
+                       }
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 try {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $event_stats   = $stats_calculator->for_event( $event->id() );
-                       $contributors  = $stats_calculator->get_contributors( $event->id() );
-                       $attendees     = $stats_calculator->get_attendees_not_contributing( $event->id() );
-                       $attendee_repo = $this->attendee_repository;
-                       $hosts         = $this->attendee_repository->get_hosts( $event->id() );
-                       $projects      = $stats_calculator->get_projects( $event->id() );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $event_stats = $this->stats_calculator->for_event( $event->id() );
</ins><span class="cx" style="display: block; padding: 0 10px">                 } catch ( Exception $e ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
</span><span class="cx" style="display: block; padding: 0 10px">                        error_log( $e );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -68,11 +102,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->die_with_error( esc_html__( 'Failed to calculate event stats', 'gp-translation-events' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $is_editable_event = true;
-               if ( $event_end->is_in_the_past() || $stats_calculator->event_has_stats( $event->id() ) ) {
-                       $is_editable_event = false;
-               }
-
</del><span class="cx" style="display: block; padding: 0 10px">                 $this->tmpl( 'event', get_defined_vars() );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventeditphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/edit.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/edit.php       2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/edit.php 2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2,12 +2,10 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> namespace Wporg\TranslationEvents\Routes\Event;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use Wporg\TranslationEvents\Attendee\Attendee;
-use Wporg\TranslationEvents\Attendee\Attendee_Repository;
</del><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Event\Event_Repository_Interface;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Routes\Route;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use Wporg\TranslationEvents\Stats\Stats_Calculator;
</del><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Translation_Events;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use Wporg\TranslationEvents\Urls;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * Displays the event edit page.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -14,12 +12,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> class Edit_Route extends Route {
</span><span class="cx" style="display: block; padding: 0 10px">        private Event_Repository_Interface $event_repository;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        private Attendee_Repository $attendee_repository;
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        public function __construct() {
</span><span class="cx" style="display: block; padding: 0 10px">                parent::__construct();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $this->event_repository    = Translation_Events::get_event_repository();
-               $this->attendee_repository = Translation_Events::get_attendee_repository();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $this->event_repository = Translation_Events::get_event_repository();
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        public function handle( int $event_id ): void {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -28,49 +24,31 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        wp_safe_redirect( wp_login_url( home_url( $wp->request ) ) );
</span><span class="cx" style="display: block; padding: 0 10px">                        exit;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $event    = $this->event_repository->get_event( $event_id );
-               $attendee = $this->attendee_repository->get_attendee( $event->id(), get_current_user_id() );
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( ! $event || ! ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'edit_post', $event->id() ) || $event->author_id() === get_current_user_id() ) ) {
-                       $this->die_with_error( esc_html__( 'Event does not exist, or you do not have permission to edit it.', 'gp-translation-events' ), 403 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $event = $this->event_repository->get_event( $event_id );
+               if ( ! $event ) {
+                       $this->die_with_404();
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( 'trash' === $event->status() ) {
-                       $this->die_with_error( esc_html__( 'You cannot edit a trashed event', 'gp-translation-events' ), 403 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               if ( ! current_user_can( 'edit_translation_event', $event->id() ) ) {
+                       $this->die_with_error( esc_html__( 'You do not have permission to edit this event.', 'gp-translation-events' ), 403 );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                include ABSPATH . 'wp-admin/includes/post.php';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $event_page_title              = 'Edit Event';
-               $event_form_name               = 'edit_event';
-               $css_show_url                  = '';
-               $event_title                   = $event->title();
-               $event_description             = $event->description();
-               $event_status                  = $event->status();
-               list( $permalink, $post_name ) = get_sample_permalink( $event->id() );
-               $permalink                     = str_replace( '%pagename%', $post_name, $permalink );
-               $event_url                     = get_site_url() . gp_url( wp_make_link_relative( $permalink ) );
-               $event_timezone                = $event->timezone();
-               $event_start                   = $event->start();
-               $event_end                     = $event->end();
-               $create_delete_button          = false;
-               $visibility_delete_button      = 'inline-flex';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $event_page_title        = 'Edit Event';
+               $event_form_name         = 'edit_event';
+               $css_show_url            = '';
+               $event_title             = $event->title();
+               $event_description       = $event->description();
+               $event_status            = $event->status();
+               $event_url               = Urls::event_details_absolute( $event_id );
+               $event_timezone          = $event->timezone();
+               $event_start             = $event->start();
+               $event_end               = $event->end();
+               $event_slug              = $event->slug();
+               $create_trash_button     = current_user_can( 'trash_translation_event', $event->id() );
+               $visibility_trash_button = 'inline-flex';
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                if ( $event->end()->is_in_the_past() ) {
-                       $this->die_with_error( esc_html__( 'You cannot edit a past event.', 'gp-translation-events' ), 403 );
-               }
-
-               $stats_calculator = new Stats_Calculator();
-
-               if ( $stats_calculator->event_has_stats( $event->id() ) ) {
-                       $this->die_with_error( esc_html__( 'You cannot edit an event with translations.', 'gp-translation-events' ), 403 );
-               }
-
-               if ( ! $stats_calculator->event_has_stats( $event->id() ) ) {
-                       $current_user = wp_get_current_user();
-                       if ( ( $current_user->ID === $event->author_id() || ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'manage_options' ) ) && ! $event->end()->is_in_the_past() ) {
-                               $create_delete_button = true;
-                       }
-               }
-
</del><span class="cx" style="display: block; padding: 0 10px">                 $this->tmpl( 'events-form', get_defined_vars() );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventlisttrashedphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/list-trashed.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/list-trashed.php                               (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/list-trashed.php 2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,45 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace Wporg\TranslationEvents\Routes\Event;
+
+use Wporg\TranslationEvents\Event\Event_Repository_Interface;
+use Wporg\TranslationEvents\Routes\Route;
+use Wporg\TranslationEvents\Translation_Events;
+
+/**
+ * Displays the page that shows the list of trashed events.
+ */
+class List_Trashed_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(): void {
+               if ( ! is_user_logged_in() ) {
+                       global $wp;
+                       wp_safe_redirect( wp_login_url( home_url( $wp->request ) ) );
+                       exit;
+               }
+
+               if ( ! current_user_can( 'manage_translation_events' ) ) {
+                       $this->die_with_error( 'You do not have permission to manage events.' );
+               }
+
+               $current_page = 1;
+               // phpcs:disable WordPress.Security.NonceVerification.Recommended
+               if ( isset( $_GET['page'] ) ) {
+                       $value = sanitize_text_field( wp_unslash( $_GET['page'] ) );
+                       if ( is_numeric( $value ) ) {
+                               $current_page = (int) $value;
+                       }
+               }
+               // phpcs:enable
+
+               $trashed_events_query = $this->event_repository->get_trashed_events( $current_page, 10 );
+
+               $this->tmpl( 'events-list-trashed', get_defined_vars() );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/list-trashed.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventtranslationsphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/translations.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/translations.php                               (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/translations.php 2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,178 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?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 );
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/translations.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesrouteseventtrashphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/trash.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/trash.php                              (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/trash.php        2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,54 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace Wporg\TranslationEvents\Routes\Event;
+
+use Wporg\TranslationEvents\Attendee\Attendee;
+use Wporg\TranslationEvents\Attendee\Attendee_Repository;
+use Wporg\TranslationEvents\Event\Event_Repository_Interface;
+use Wporg\TranslationEvents\Routes\Route;
+use Wporg\TranslationEvents\Stats\Stats_Importer;
+use Wporg\TranslationEvents\Translation_Events;
+use Wporg\TranslationEvents\Urls;
+
+/**
+ * Toggle whether the event is trashed.
+ * If the event is not currently trashed, it will be trashed.
+ * If the event is currently trashed, it will be un-trashed.
+ */
+class Trash_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( int $event_id ): void {
+               if ( ! is_user_logged_in() ) {
+                       global $wp;
+                       wp_safe_redirect( wp_login_url( home_url( $wp->request ) ) );
+                       exit;
+               }
+
+               $event = $this->event_repository->get_event( $event_id );
+               if ( ! $event ) {
+                       $this->die_with_404();
+               }
+
+               if ( ! current_user_can( 'trash_translation_event', $event->id() ) ) {
+                       $this->die_with_error( esc_html__( 'You do not have permission to delete or restore this event.', 'gp-translation-events' ), 403 );
+               }
+
+               if ( ! $event->is_trashed() ) {
+                       // Trash.
+                       $this->event_repository->trash_event( $event );
+               } else {
+                       // Restore.
+                       $event->set_status( 'draft' );
+                       $this->event_repository->update_event( $event );
+               }
+
+               wp_safe_redirect( Urls::event_edit( $event->id() ) );
+               exit;
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/event/trash.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesroutesuserattendeventphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/attend-event.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/attend-event.php        2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/attend-event.php  2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -8,6 +8,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Routes\Route;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Stats\Stats_Importer;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Translation_Events;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use Wporg\TranslationEvents\Urls;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * Toggle whether the current user is attending an event.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -47,6 +48,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $attendee = $this->attendee_repository->get_attendee( $event->id(), $user_id );
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $attendee instanceof Attendee ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        if ( $attendee->is_contributor() ) {
+                               $this->die_with_error( esc_html__( 'Contributors cannot un-attend the event', 'gp-translation-events' ), 403 );
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                         $this->attendee_repository->remove_attendee( $event->id(), $user_id );
</span><span class="cx" style="display: block; padding: 0 10px">                } else {
</span><span class="cx" style="display: block; padding: 0 10px">                        $attendee = new Attendee( $event->id(), $user_id );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -59,7 +63,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp_safe_redirect( gp_url( "/events/{$event->slug()}" ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         wp_safe_redirect( Urls::event_details( $event->id() ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 exit;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesroutesuserhosteventphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/host-event.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/host-event.php  2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/routes/user/host-event.php    2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -7,6 +7,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Event\Event_Repository_Interface;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Routes\Route;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Translation_Events;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use Wporg\TranslationEvents\Urls;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * Toggle whether the current user is hosting an event.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -36,12 +37,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">        public function handle( int $event_id, int $user_id ): void {
</span><span class="cx" style="display: block; padding: 0 10px">                $current_user = wp_get_current_user();
</span><span class="cx" style="display: block; padding: 0 10px">                if ( ! $current_user->exists() ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $this->die_with_error( esc_html__( "Only logged-in users can manage event's hosts.", 'gp-translation-events' ), 403 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $this->die_with_error( esc_html__( "Only logged-in users can manage the event's hosts.", 'gp-translation-events' ), 403 );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $current_user_attendee = $this->attendee_repository->get_attendee( $event_id, $current_user->ID );
-               if ( ! current_user_can( 'manage_options' ) && ! $current_user_attendee->is_host() ) {
-                       $this->die_with_error( esc_html__( "This user does not have permissions to manage event's hosts.", 'gp-translation-events' ), 403 );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         if ( ! current_user_can( 'edit_translation_event', $event_id ) ) {
+                       $this->die_with_error( esc_html__( "You do not have permissions to manage the event's hosts.", 'gp-translation-events' ), 403 );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $event = $this->event_repository->get_event( $event_id );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -59,9 +59,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        $this->attendee_repository->update_attendee( $affected_attendee );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        $this->event_repository->update_event( $event );
</ins><span class="cx" style="display: block; padding: 0 10px">                 }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                wp_safe_redirect( gp_url( "/events/{$event->slug()}" ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         wp_safe_redirect( Urls::event_attendees( $event->id() ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 exit;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesstatsstatscalculatorphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/stats/stats-calculator.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/stats/stats-calculator.php  2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/stats/stats-calculator.php    2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -3,23 +3,20 @@
</span><span class="cx" style="display: block; padding: 0 10px"> namespace Wporg\TranslationEvents\Stats;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> use Exception;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use WP_Post;
-use WP_User;
-use GP;
</del><span class="cx" style="display: block; padding: 0 10px"> use GP_Locale;
</span><span class="cx" style="display: block; padding: 0 10px"> use GP_Locales;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use DateTimeImmutable;
-use DateTimeZone;
</del><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> class Stats_Row {
</span><span class="cx" style="display: block; padding: 0 10px">        public int $created;
</span><span class="cx" style="display: block; padding: 0 10px">        public int $reviewed;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        public int $waiting;
</ins><span class="cx" style="display: block; padding: 0 10px">         public int $users;
</span><span class="cx" style="display: block; padding: 0 10px">        public ?GP_Locale $language = null;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        public function __construct( $created, $reviewed, $users, ?GP_Locale $language = null ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public function __construct( $created, $reviewed, $waiting, $users, ?GP_Locale $language = null ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                 $this->created  = $created;
</span><span class="cx" style="display: block; padding: 0 10px">                $this->reviewed = $reviewed;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $this->waiting  = $waiting;
</ins><span class="cx" style="display: block; padding: 0 10px">                 $this->users    = $users;
</span><span class="cx" style="display: block; padding: 0 10px">                $this->language = $language;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -103,8 +100,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                select locale,
</span><span class="cx" style="display: block; padding: 0 10px">                                        sum(action = 'create') as created,
</span><span class="cx" style="display: block; padding: 0 10px">                                        count(*) as total,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        count(distinct user_id) as users
-                               from {$gp_table_prefix}event_actions
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 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
</ins><span class="cx" style="display: block; padding: 0 10px">                                 where event_id = %d
</span><span class="cx" style="display: block; padding: 0 10px">                                group by locale with rollup
</span><span class="cx" style="display: block; padding: 0 10px">                        ",
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -130,9 +129,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                $lang = null;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        if ( is_null( $row->waiting ) ) {
+                               // The corresponding translations are missing. Could be a unit test or data corruption.
+                               $row->waiting = 0;
+                       }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                         $stats_row = new Stats_Row(
</span><span class="cx" style="display: block; padding: 0 10px">                                $row->created,
</span><span class="cx" style="display: block; padding: 0 10px">                                $row->total - $row->created,
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                $row->waiting,
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $row->users,
</span><span class="cx" style="display: block; padding: 0 10px">                                $lang
</span><span class="cx" style="display: block; padding: 0 10px">                        );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -148,143 +153,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        /**
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-         * Get contributors for an event.
-        */
-       public function get_contributors( int $event_id ): array {
-               global $wpdb, $gp_table_prefix;
-
-               // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
-               // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
-               // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
-               // phpcs thinks we're doing a schema change but we aren't.
-               // phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange
-               $rows = $wpdb->get_results(
-                       $wpdb->prepare(
-                               "
-                               select user_id, group_concat( distinct locale ) as locales
-                               from {$gp_table_prefix}event_actions
-                               where event_id = %d
-                               group by user_id
-                       ",
-                               array(
-                                       $event_id,
-                               )
-                       )
-               );
-               // phpcs:enable
-
-               $users = array();
-               foreach ( $rows as $row ) {
-                       $user          = new WP_User( $row->user_id );
-                       $user->locales = explode( ',', $row->locales );
-                       $users[]       = $user;
-               }
-
-               uasort(
-                       $users,
-                       function ( $a, $b ) {
-                               return strcasecmp( $a->display_name, $b->display_name );
-                       }
-               );
-
-               return $users;
-       }
-
-       /**
-        * Get attendees without contributions for an event.
-        */
-       public function get_attendees_not_contributing( int $event_id ): array {
-               global $wpdb, $gp_table_prefix;
-
-               // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
-               // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
-               // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
-               $all_attendees_ids = $wpdb->get_col(
-                       $wpdb->prepare(
-                               "
-                               select distinct user_id
-                               from {$gp_table_prefix}event_attendees
-                               where event_id = %d
-                       ",
-                               array(
-                                       $event_id,
-                               )
-                       ),
-               );
-
-               $contributing_ids = $wpdb->get_col(
-                       $wpdb->prepare(
-                               "
-                               select distinct user_id
-                               from {$gp_table_prefix}event_actions
-                               where event_id = %d
-                       ",
-                               array(
-                                       $event_id,
-                               )
-                       )
-               );
-
-               $attendees_not_contributing_ids = array_diff( $all_attendees_ids, $contributing_ids );
-
-               $attendees_not_contributing = array();
-               foreach ( $attendees_not_contributing_ids as $user_id ) {
-                       $attendees_not_contributing[] = new WP_User( $user_id );
-               }
-
-               return $attendees_not_contributing;
-       }
-
-       /**
-        * Get projects for an event.
-        */
-       public function get_projects( int $event_id ): array {
-               global $wpdb, $gp_table_prefix;
-
-               // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
-               // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
-               // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
-               // phpcs thinks we're doing a schema change but we aren't.
-               // phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange
-               $rows = $wpdb->get_results(
-                       $wpdb->prepare(
-                               "
-                               select
-                                       o.project_id as project,
-                                       group_concat( distinct e.locale ) as locales,
-                                       sum(action = 'create') as created,
-                                       count(*) as total,
-                                       count(distinct user_id) as users
-                               from {$gp_table_prefix}event_actions e, {$gp_table_prefix}originals o
-                               where e.event_id = %d and e.original_id = o.id
-                               group by o.project_id
-                       ",
-                               array(
-                                       $event_id,
-                               )
-                       )
-               );
-               // phpcs:enable
-
-               $projects = array();
-               foreach ( $rows as $row ) {
-                       $row->project      = GP::$project->get( $row->project );
-                       $project_name      = $row->project->name;
-                       $parent_project_id = $row->project->parent_project_id;
-                       while ( $parent_project_id ) {
-                               $parent_project    = GP::$project->get( $parent_project_id );
-                               $parent_project_id = $parent_project->parent_project_id;
-                               $project_name      = substr( htmlspecialchars_decode( $parent_project->name ), 0, 35 ) . ' - ' . $project_name;
-                       }
-                       $projects[ $project_name ] = $row;
-               }
-
-               ksort( $projects );
-
-               return $projects;
-       }
-
-       /**
</del><span class="cx" style="display: block; padding: 0 10px">          * Check if an event has stats.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @param int $event_id The id of the event to check.
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -300,38 +168,4 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                return ! empty( $stats->rows() );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-       /**
-        * Check if a user is a new translation contributor. A new contributor is a user who has made 10 or fewer translations before event start time.
-        *
-        * @param Event_Start_Date $event_start The event start date.
-        * @param int              $user_id      The user ID.
-        *
-        * @return bool True if the user is a new translation contributor, false otherwise.
-        */
-       public function is_new_translation_contributor( $event_start, $user_id ) {
-               global $wpdb, $gp_table_prefix;
-               $new_contributor_max_translation_count = 10;
-               $event_start_date_time                 = $event_start->__toString();
-               // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
-               // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
-               // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
-               // phpcs:disable WordPress.DB.DirectDatabaseQuery.SchemaChange
-               $user_translations_count = $wpdb->get_var(
-                       $wpdb->prepare(
-                               "
-                       select count(*) from {$gp_table_prefix}translations where user_id = %d and date_added < %s
-               ",
-                               array(
-                                       $user_id,
-                                       $event_start_date_time,
-                               )
-                       )
-               );
-
-               if ( get_userdata( $user_id ) && ! $user_translations_count ) {
-                       return true;
-               }
-               return $user_translations_count <= $new_contributor_max_translation_count;
-       }
</del><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludestranslationtranslationrepositoryphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/translation/translation-repository.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/translation/translation-repository.php                              (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/translation/translation-repository.php        2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,60 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace Wporg\TranslationEvents\Translation;
+
+use DateTimeImmutable;
+
+class Translation_Repository {
+       /**
+        * Count the number of translations made by given users before a specified datetime.
+        *
+        * @param int[]             $user_ids Ids of users for which to count translations.
+        * @param DateTimeImmutable $before   Only include translations before this datetime.
+        *
+        * @return array Associative array with user id as key and number of translations as value.
+        */
+       public function count_translations_before( array $user_ids, DateTimeImmutable $before ): array {
+               if ( empty( $user_ids ) ) {
+                       return array();
+               }
+
+               // Prevent SQL injection.
+               foreach ( $user_ids as $user_id ) {
+                       if ( ! is_int( $user_id ) ) {
+                               return array();
+                       }
+               }
+
+               global $wpdb, $gp_table_prefix;
+               $user_id_params = implode( ',', array_fill( 0, count( $user_ids ), '%d' ) );
+
+               // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+               // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
+               // phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
+               // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+               $rows = $wpdb->get_results(
+                       $wpdb->prepare(
+                               "
+                                       select user_id, count(*) as cnt
+                                       from {$gp_table_prefix}translations
+                                       where user_id in ($user_id_params)
+                                         and date_added < %s
+                                       group by user_id
+                               ",
+                               array_merge(
+                                       $user_ids,
+                                       array( $before->format( 'Y-m-d H:i:s' ) ),
+                               ),
+                       ),
+                       OBJECT_K
+               );
+               // phpcs:enable
+
+               $results = array_fill_keys( $user_ids, 0 );
+               foreach ( $rows as $user_id => $row ) {
+                       $results[ $user_id ] = $row->cnt;
+               }
+
+               return $results;
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/translation/translation-repository.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesupgradephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/upgrade.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/upgrade.php 2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/upgrade.php   2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2,11 +2,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> namespace Wporg\TranslationEvents;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use Exception;
-use WP_Query;
-use Wporg\TranslationEvents\Attendee\Attendee;
-use Wporg\TranslationEvents\Stats\Stats_Calculator;
-
</del><span class="cx" style="display: block; padding: 0 10px"> class Upgrade {
</span><span class="cx" style="display: block; padding: 0 10px">        private const VERSION        = 2;
</span><span class="cx" style="display: block; padding: 0 10px">        private const VERSION_OPTION = 'wporg_gp_translations_events_version';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -28,17 +23,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                require_once ABSPATH . 'wp-admin/includes/upgrade.php';
</span><span class="cx" style="display: block; padding: 0 10px">                dbDelta( self::get_database_schema_sql() );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                // Run version-specific upgrades.
-               $is_running_tests = 'yes' === getenv( 'WPORG_TRANSLATION_EVENTS_TESTS' );
-               if ( $previous_version < 2 && ! $is_running_tests ) {
-                       try {
-                               self::v2_import_legacy_attendees();
-                       } catch ( Exception $e ) {
-                               // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
-                               error_log( $e );
-                       }
-               }
-
</del><span class="cx" style="display: block; padding: 0 10px">                 update_option( self::VERSION_OPTION, self::VERSION );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -69,38 +53,4 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        ) COMMENT='Attendees of events';
</span><span class="cx" style="display: block; padding: 0 10px">                ";
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
-       /**
-        * Previously, event attendance was tracked through user_meta.
-        * This function imports this legacy attendance information into the attendees table.
-        *
-        * Instead of looping through all users, we consider only users who have contributed to an event.
-        *
-        * @throws Exception
-        */
-       private static function v2_import_legacy_attendees(): void {
-               $query = new WP_Query(
-                       array(
-                               'post_type'   => Translation_Events::CPT,
-                               'post_status' => 'publish',
-                       )
-               );
-
-               $events              = $query->get_posts();
-               $stats_calculator    = new Stats_Calculator();
-               $attendee_repository = Translation_Events::get_attendee_repository();
-               foreach ( $events as $event ) {
-                       $host_attendee = new Attendee( $event->ID, intval( $event->post_author ) );
-                       $host_attendee->mark_as_host();
-                       $attendee_repository->insert_attendee( $host_attendee );
-
-                       foreach ( $stats_calculator->get_contributors( $event->ID ) as $user ) {
-                               $attendee = $attendee_repository->get_attendee( $event->ID, $user->id );
-                               if ( ! $attendee ) {
-                                       $attendee = new Attendee( $event->ID, $user->ID );
-                                       $attendee_repository->insert_attendee( $attendee );
-                               }
-                       }
-               }
-       }
</del><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventsincludesurlsphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/urls.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/urls.php                            (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/urls.php      2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,60 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace Wporg\TranslationEvents;
+
+class Urls {
+       public static function events_home(): string {
+               return gp_url( '/events' );
+       }
+
+       public static function events_trashed(): string {
+               return gp_url( '/events/trashed' );
+       }
+
+       public static function event_details( int $event_id ): string {
+               return gp_url( wp_make_link_relative( get_the_permalink( $event_id ) ) );
+       }
+
+       public static function event_details_absolute( int $event_id ): string {
+               list( $permalink, $post_name ) = get_sample_permalink( $event_id );
+               $permalink                     = str_replace( '%pagename%', $post_name, $permalink );
+
+               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 );
+       }
+
+       public static function event_trash( int $event_id ): string {
+               return gp_url( '/events/trash/' . $event_id );
+       }
+
+       public static function event_delete( int $event_id ): string {
+               return gp_url( '/events/delete/' . $event_id );
+       }
+
+       public static function event_create(): string {
+               return gp_url( '/events/new/' );
+       }
+
+       public static function event_toggle_attendee( int $event_id ): string {
+               return gp_url( "/events/attend/$event_id" );
+       }
+
+       public static function event_toggle_host( int $event_id, int $user_id ): string {
+               return gp_url( "/events/host/$event_id/$user_id" );
+       }
+
+       public static function my_events(): string {
+               return gp_url( '/events/my-events/' );
+       }
+
+       public static function event_attendees( int $event_id ): string {
+               return self::event_details( $event_id ) . 'attendees/';
+       }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/includes/urls.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventtranslationsfooterphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event-translations-footer.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event-translations-footer.php                              (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event-translations-footer.php        2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,36 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+</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(); ?>
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event-translations-footer.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventtranslationsheaderphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event-translations-header.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event-translations-header.php                              (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event-translations-header.php        2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,63 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?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>
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event-translations-header.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event.php  2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/event.php    2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -8,7 +8,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> use GP_Locales;
</span><span class="cx" style="display: block; padding: 0 10px"> use WP_User;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Attendee\Attendee;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use Wporg\TranslationEvents\Attendee\Attendee_Repository;
</del><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Event\Event;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Event\Event_End_Date;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Event\Event_Start_Date;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -15,8 +14,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Stats\Event_Stats;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Stats\Stats_Row;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/** @var Attendee_Repository $attendee_repo */
-/** @var Attendee $attendee */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/** @var bool $user_is_attending */
+/** @var bool $user_is_contributor */
+/** @var Attendee[] $attendees_not_contributing */
+/** @var Attendee[] $contributors */
+/** @var array $new_contributor_ids */
</ins><span class="cx" style="display: block; padding: 0 10px"> /** @var Event $event */
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var int $event_id */
</span><span class="cx" style="display: block; padding: 0 10px"> /** @var string $event_title */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -52,67 +54,33 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                </h2>
</span><span class="cx" style="display: block; padding: 0 10px">                                <ul>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <?php foreach ( $contributors as $contributor ) : ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                <li class="event-contributor" title="<?php echo esc_html( implode( ', ', $contributor->locales ) ); ?>">
-                                                       <a href="<?php echo esc_url( get_author_posts_url( $contributor->ID ) ); ?>" class="avatar"><?php echo get_avatar( $contributor->ID, 48 ); ?></a>
-                                                       <a href="<?php echo esc_url( get_author_posts_url( $contributor->ID ) ); ?>" class="name"><?php echo esc_html( get_the_author_meta( 'display_name', $contributor->ID ) ); ?></a>
-                                                       <?php if ( $stats_calculator->is_new_translation_contributor( $event_start, $contributor->ID ) ) : ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         <li class="event-contributor" title="<?php echo esc_html( implode( ', ', $contributor->contributed_locales() ) ); ?>">
+                                                       <a href="<?php echo esc_url( get_author_posts_url( $contributor->user_id() ) ); ?>" class="avatar"><?php echo get_avatar( $contributor->user_id(), 48 ); ?></a>
+                                                       <a href="<?php echo esc_url( get_author_posts_url( $contributor->user_id() ) ); ?>" class="name"><?php echo esc_html( get_the_author_meta( 'display_name', $contributor->user_id() ) ); ?></a>
+                                                       <?php if ( isset( $new_contributor_ids[ $contributor->user_id() ] ) ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                                                                 <span class="first-time-contributor-tada" title="<?php esc_html_e( 'New Translation Contributor', 'gp-translation-events' ); ?>"></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                                        <?php endif; ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        <?php
-                                                       if ( ! $event->end()->is_in_the_past() ) :
-                                                               if ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'manage_options' ) || $user->ID === $event->author_id() ) :
-                                                                       $_attendee = $attendee_repo->get_attendee( $event_id, $contributor->ID );
-                                                                       if ( $_attendee instanceof Attendee ) :
-                                                                               echo '<form class="add-remove-user-as-host" method="post" action="' . esc_url( gp_url( "/events/host/$event_id/$contributor->ID" ) ) . '">';
-                                                                               if ( $_attendee->is_host() ) :
-                                                                                       echo '<input type="submit" class="button is-primary remove-as-host" value="Remove as host"/>';
-                                                                               else :
-                                                                                       echo '<input type="submit" class="button is-secondary convert-to-host" value="Make co-host"/>';
-                                                                               endif;
-                                                                               echo '</form>';
-                                                                       else :
-                                                                               echo '<span class="event-not-attending">' . esc_html__( 'Not attending', 'gp-translation-events' ) . '</span>';
-                                                                       endif;
-                                                               endif;
-                                                       endif;
-                                                       ?>
</del><span class="cx" style="display: block; padding: 0 10px">                                                 </li>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <?php endforeach; ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                </ul>
</span><span class="cx" style="display: block; padding: 0 10px">                        </div>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php endif; ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <?php if ( ! empty( $attendees ) && ( ! $event->end()->is_in_the_past() || ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'manage_options' ) || $user->ID === $event->author_id() ) ) ) : ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php if ( ! empty( $attendees_not_contributing ) && current_user_can( 'edit_translation_event', $event->id() ) ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                         <div class="event-attendees">
</span><span class="cx" style="display: block; padding: 0 10px">                                <h2>
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php
</span><span class="cx" style="display: block; padding: 0 10px">                                // translators: %d is the number of attendees.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                echo esc_html( sprintf( __( 'Attendees (%d)', 'gp-translation-events' ), number_format_i18n( count( $attendees ) ) ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         echo esc_html( sprintf( __( 'Attendees (%d)', 'gp-translation-events' ), number_format_i18n( count( $attendees_not_contributing ) ) ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                </h2>
</span><span class="cx" style="display: block; padding: 0 10px">                                <ul>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        <?php foreach ( $attendees as $_user ) : ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <?php foreach ( $attendees_not_contributing as $_attendee ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 <li class="event-attendee">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        <a href="<?php echo esc_url( get_author_posts_url( $_user->ID ) ); ?>" class="avatar"><?php echo get_avatar( $_user->ID, 48 ); ?></a>
-                                                       <a href="<?php echo esc_url( get_author_posts_url( $_user->ID ) ); ?>" class="name"><?php echo esc_html( get_the_author_meta( 'display_name', $_user->ID ) ); ?></a>
-                                                       <?php if ( $stats_calculator->is_new_translation_contributor( $event_start, $_user->ID ) ) : ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                 <a href="<?php echo esc_url( get_author_posts_url( $_attendee->user_id() ) ); ?>" class="avatar"><?php echo get_avatar( $_attendee->user_id(), 48 ); ?></a>
+                                                       <a href="<?php echo esc_url( get_author_posts_url( $_attendee->user_id() ) ); ?>" class="name"><?php echo esc_html( get_the_author_meta( 'display_name', $_attendee->user_id() ) ); ?></a>
+                                                       <?php if ( isset( $new_contributor_ids[ $_attendee->user_id() ] ) ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                                                                 <span class="first-time-contributor-tada" title="<?php esc_html_e( 'New Translation Contributor', 'gp-translation-events' ); ?>"></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                                        <?php endif; ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        <?php
-                                                       if ( ! $event->end()->is_in_the_past() ) :
-                                                               if ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'manage_options' ) || $user->ID === $event->author_id() ) :
-                                                                       $_attendee = $attendee_repo->get_attendee( $event_id, $_user->ID );
-                                                                       if ( $_attendee instanceof Attendee ) :
-                                                                               echo '<form class="add-remove-user-as-host" method="post" action="' . esc_url( gp_url( "/events/host/$event_id/$_user->ID" ) ) . '">';
-                                                                               if ( $_attendee->is_host() ) :
-                                                                                       echo '<input type="submit" class="button is-primary remove-as-host" value="Remove as host"/>';
-                                                                               else :
-                                                                                       echo '<input type="submit" class="button is-secondary convert-to-host" value="Make co-host"/>';
-                                                                               endif;
-                                                                               echo '</form>';
-                                                                       endif;
-                                                               endif;
-                                                       endif;
-                                                       ?>
</del><span class="cx" style="display: block; padding: 0 10px">                                                 </li>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <?php endforeach; ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                </ul>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -124,10 +92,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                <table>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <thead>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <tr>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                <th scope="col">Locale</th>
-                                               <th scope="col">Translations created</th>
-                                               <th scope="col">Translations reviewed</th>
-                                               <th scope="col">Contributors</th>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         <th scope="col"><?php esc_html_e( 'Translations', 'gp-translation-events' ); ?></th>
+                                               <th scope="col"><?php esc_html_e( 'Created', 'gp-translation-events' ); ?></th>
+                                               <th scope="col"><?php esc_html_e( 'Waiting', 'gp-translation-events' ); ?></th>
+                                               <th scope="col"><?php esc_html_e( 'Reviewed', 'gp-translation-events' ); ?></th>
+                                               <th scope="col"><?php esc_html_e( 'Contributors', 'gp-translation-events' ); ?></th>
</ins><span class="cx" style="display: block; padding: 0 10px">                                         </tr>
</span><span class="cx" style="display: block; padding: 0 10px">                                        </thead>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <tbody>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -135,7 +104,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        <?php foreach ( $event_stats->rows() as $_locale => $row ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <tr>
</span><span class="cx" style="display: block; padding: 0 10px">                                                <td title="<?php echo esc_html( $_locale ); ?> "><a href="<?php echo esc_url( gp_url_join( gp_url( '/languages' ), $row->language->slug ) ); ?>"><?php echo esc_html( $row->language->english_name ); ?></a></td>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                <td><?php echo esc_html( $row->created ); ?></td>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         <td><a href="<?php echo esc_url( Urls::event_translations( $event->id(), $row->language->slug ) ); ?>"><?php echo esc_html( $row->created ); ?></a></td>
+                                               <td><a href="<?php echo esc_url( Urls::event_translations( $event->id(), $row->language->slug, 'waiting' ) ); ?>"><?php echo esc_html( $row->waiting ); ?></a></td>
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 <td><?php echo esc_html( $row->reviewed ); ?></td>
</span><span class="cx" style="display: block; padding: 0 10px">                                                <td><?php echo esc_html( $row->users ); ?></td>
</span><span class="cx" style="display: block; padding: 0 10px">                                        </tr>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -143,6 +113,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        <tr class="event-details-stats-totals">
</span><span class="cx" style="display: block; padding: 0 10px">                                                <td>Total</td>
</span><span class="cx" style="display: block; padding: 0 10px">                                                <td><?php echo esc_html( $event_stats->totals()->created ); ?></td>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                <td><?php echo esc_html( $event_stats->totals()->waiting ); ?></td>
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 <td><?php echo esc_html( $event_stats->totals()->reviewed ); ?></td>
</span><span class="cx" style="display: block; padding: 0 10px">                                                <td><?php echo esc_html( $event_stats->totals()->users ); ?></td>
</span><span class="cx" style="display: block; padding: 0 10px">                                        </tr>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -154,18 +125,27 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                <ul>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <?php foreach ( $projects as $project_name => $row ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <li class="event-project" title="<?php echo esc_html( str_replace( ',', ', ', $row->locales ) ); ?>">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                <a href="<?php echo esc_url( gp_url_project( $row->project ) ); ?>"><?php echo esc_html( $project_name ); ?></a> <small> to
</del><span class="cx" style="display: block; padding: 0 10px">                                                 <?php
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                $row_locales = array();
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 foreach ( explode( ',', $row->locales ) as $_locale ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        $_locale = GP_Locales::by_slug( $_locale );
-                                                       ?>
-                                                       <a href="<?php echo esc_url( gp_url_project_locale( $row->project, $_locale->slug, 'default' ) ); ?>"><?php echo esc_html( $_locale->english_name ); ?></a>
-                                                       <?php
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                 $_locale       = GP_Locales::by_slug( $_locale );
+                                                       $row_locales[] = '<a href="' . esc_url( gp_url_project_locale( $row->project, $_locale->slug, 'default' ) ) . '">' . esc_html( $_locale->english_name ) . '</a>';
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                // translators: %d: Number of contributors.
-                                               echo esc_html( sprintf( _n( 'by %d contributor', 'by %d contributors', $row->users, 'gp-translation-events' ), $row->users ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         echo wp_kses_post(
+                                                       wp_sprintf(
+                                                               // translators: 1: Project translated. 2: List of languages. 3: Number of contributors.
+                                                               _n(
+                                                                       '%1$s <small>to %2$l by %3$d contributor</small>',
+                                                                       '%1$s <small>to %2$l by %3$d contributors</small>',
+                                                                       $row->users,
+                                                                       'gp-translation-events'
+                                                               ),
+                                                               '<a href="' . esc_url( gp_url_project( $row->project ) ) . '">' . esc_html( $project_name ) . '</a>',
+                                                               $row_locales,
+                                                               $row->users
+                                                       )
+                                               );
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                </small>
</del><span class="cx" style="display: block; padding: 0 10px">                                         </li>
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php endforeach; ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                </ul>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -174,40 +154,28 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                <summary><?php esc_html_e( 'View stats summary in text', 'gp-translation-events' ); ?></summary>
</span><span class="cx" style="display: block; padding: 0 10px">                                <p class="event-stats-text">
</span><span class="cx" style="display: block; padding: 0 10px">                                        <?php
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        $new_contributors = array_filter(
-                                               $contributors,
-                                               function ( $contributor ) use ( $stats_calculator, $event_start ) {
-                                                       return $stats_calculator->is_new_translation_contributor( $event_start, $contributor->ID );
-                                               }
-                                       );
-
</del><span class="cx" style="display: block; padding: 0 10px">                                         $new_contributors_text = '';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        if ( ! empty( $new_contributors ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 if ( ! empty( $new_contributor_ids ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 $new_contributors_text = sprintf(
</span><span class="cx" style="display: block; padding: 0 10px">                                                        // translators: %d is the number of new contributors.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        _n( '(%d new contributor ðŸŽ‰)', '(%d new contributors ðŸŽ‰)', count( $new_contributors ), 'gp-translation-events' ),
-                                                       count( $new_contributors )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                 _n( '(%d new contributor ðŸŽ‰)', '(%d new contributors ðŸŽ‰)', count( $new_contributor_ids ), 'gp-translation-events' ),
+                                                       count( $new_contributor_ids )
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 );
</span><span class="cx" style="display: block; padding: 0 10px">                                        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                                        echo wp_kses(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                sprintf(
-                                                       // translators: %1$s: Event title, %2$d: Number of contributors, %3$s: is a parenthesis with potential text "x new contributors", %4$d: Number of languages, %5$s: List of languages, %6$d: Number of strings translated, %7$d: Number of strings reviewed.
-                                                       __( 'At the <strong>%1$s</strong> event, we had %2$d people %3$s who contributed in %4$d languages (%5$s), translated %6$d strings and reviewed %7$d strings.', 'gp-translation-events' ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         wp_sprintf(
+                                                       // translators: %1$s: Event title, %2$d: Number of contributors, %3$s: is a parenthesis with potential text "x new contributors", %4$d: Number of languages, %5$l: List of languages, %6$d: Number of strings translated, %7$d: Number of strings reviewed.
+                                                       __( 'At the <strong>%1$s</strong> event, we had %2$d people %3$s who contributed in %4$d languages (%5$l), translated %6$d strings and reviewed %7$d strings.', 'gp-translation-events' ),
</ins><span class="cx" style="display: block; padding: 0 10px">                                                         esc_html( $event_title ),
</span><span class="cx" style="display: block; padding: 0 10px">                                                        esc_html( $event_stats->totals()->users ),
</span><span class="cx" style="display: block; padding: 0 10px">                                                        $new_contributors_text,
</span><span class="cx" style="display: block; padding: 0 10px">                                                        count( $event_stats->rows() ),
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                        esc_html(
-                                                               implode(
-                                                                       ', ',
-                                                                       array_map(
-                                                                               function ( $row ) {
-                                                                                       return $row->language->english_name;
-                                                                               },
-                                                                               $event_stats->rows()
-                                                                       )
-                                                               )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                 array_map(
+                                                               function ( $row ) {
+                                                                       return $row->language->english_name;
+                                                               },
+                                                               $event_stats->rows()
</ins><span class="cx" style="display: block; padding: 0 10px">                                                         ),
</span><span class="cx" style="display: block; padding: 0 10px">                                                        esc_html( $event_stats->totals()->created ),
</span><span class="cx" style="display: block; padding: 0 10px">                                                        esc_html( $event_stats->totals()->reviewed )
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -219,21 +187,23 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                        ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <?php
</span><span class="cx" style="display: block; padding: 0 10px">                                        echo wp_kses(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                sprintf(
-                                               // translators: %s the contributors.
-                                                       esc_html__( 'Contributors were %s.', 'gp-translation-events' ),
-                                                       implode(
-                                                               ', ',
-                                                               array_map(
-                                                                       function ( $contributor ) use ( $stats_calculator, $event_start ) {
-                                                                               $append_tada = '';
-                                                                               if ( $stats_calculator->is_new_translation_contributor( $event_start, $contributor->ID ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         wp_sprintf(
+                                                       // translators: %s List of contributors.
+                                                       _n(
+                                                               'Contributor was %l.',
+                                                               'Contributors were %l.',
+                                                               count( $contributors ),
+                                                               'gp-translation-events'
+                                                       ),
+                                                       array_map(
+                                                               function ( $contributor ) {
+                                                                       $append_tada = '';
+                                                                       if ( isset( $new_contributor_ids[ $contributor->user_id() ] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px">                                                                                         $append_tada = ' <span class="new-contributor" title="' . esc_html__( 'New Translation Contributor', 'gp-translation-events' ) . '">🎉</span>';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                                                }
-                                                                               return '@' . $contributor->user_login . $append_tada;
-                                                                       },
-                                                                       $contributors
-                                                               )
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                                                 }
+                                                                       return '@' . ( new WP_User( $contributor->user_id() ) )->user_login . $append_tada;
+                                                               },
+                                                               $contributors
</ins><span class="cx" style="display: block; padding: 0 10px">                                                         )
</span><span class="cx" style="display: block; padding: 0 10px">                                                ),
</span><span class="cx" style="display: block; padding: 0 10px">                                                array(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -267,15 +237,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <?php if ( is_user_logged_in() ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                <div class="event-details-join">
</span><span class="cx" style="display: block; padding: 0 10px">                        <?php if ( $event_end->is_in_the_past() ) : ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <?php if ( $attendee instanceof Attendee ) : ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <?php if ( $user_is_attending ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                                         <button disabled="disabled" class="button is-primary attend-btn"><?php esc_html_e( 'You attended', 'gp-translation-events' ); ?></button>
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php endif; ?>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        <?php elseif ( $user_is_contributor ) : ?>
+                               <?php // Contributors can't un-attend so don't show anything. ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                         <?php else : ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <form class="event-details-attend" method="post" action="<?php echo esc_url( gp_url( "/events/attend/$event_id" ) ); ?>">
-                                       <?php if ( $attendee instanceof Attendee ) : ?>
-                                               <input type="submit" class="button is-secondary attending-btn" value="You're attending" />
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <form class="event-details-attend" method="post" action="<?php echo esc_url( Urls::event_toggle_attendee( $event_id ) ); ?>">
+                                       <?php if ( $user_is_attending ) : ?>
+                                               <input type="submit" class="button is-secondary attending-btn" value="<?php esc_attr_e( "You're attending", 'gp-translation-events' ); ?>" />
</ins><span class="cx" style="display: block; padding: 0 10px">                                         <?php else : ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                <input type="submit" class="button is-primary attend-btn" value="Attend Event"/>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         <input type="submit" class="button is-primary attend-btn" value="<?php esc_attr_e( 'Attend Event', 'gp-translation-events' ); ?>"/>
</ins><span class="cx" style="display: block; padding: 0 10px">                                         <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                </form>
</span><span class="cx" style="display: block; padding: 0 10px">                        <?php endif; ?>
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventsattendeesphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-attendees.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-attendees.php                               (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-attendees.php 2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,66 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Attendees list page.
+ */
+
+namespace Wporg\TranslationEvents;
+
+use Wporg\TranslationEvents\Event\Event;
+
+/**  @var Event $event */
+/** @var bool $is_active_filter */
+
+gp_title( __( 'Translation Events', 'gp-translation-events' ) );
+gp_breadcrumb_translation_events();
+gp_tmpl_header();
+$event_page_title = __( 'Manage Attendees', 'gp-translation-events' );
+gp_tmpl_load( 'events-header', get_defined_vars(), __DIR__ );
+
+?>
+<div class="event-page-wrapper">
+       <div class="event-details-stats">
+       <a href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>" class="view-event-page">Go to event page</a>
+       <ul class="event-attendees-filter">
+               <li><a class="<?php echo ( ! $is_active_filter ) ? 'active-filter' : ''; ?>" href="<?php echo esc_url( Urls::event_attendees( $event->id() ) ); ?>"><?php esc_html_e( 'All attendees', 'gp-translation-events' ); ?></a></a></li>
+               <li><a class="<?php echo ( $is_active_filter ) ? 'active-filter' : ''; ?>" href="?filter=hosts"><?php esc_html_e( 'Hosts', 'gp-translation-events' ); ?></a></li>
+       </ul>
+<?php if ( ! empty( $attendees ) ) : ?>
+       <table>
+               <thead>
+                       <tr>
+                               <th scope="col"><?php esc_html_e( 'Name', 'gp-translation-events' ); ?></th>
+                               <th><?php esc_html_e( 'Host', 'gp-translation-events' ); ?></th>
+                               <th><?php esc_html_e( 'Action', 'gp-translation-events' ); ?></th>
+                       </tr>
+               </thead>
+               <tbody>
+                       <?php foreach ( $attendees as $attendee ) : ?>
+                               <tr>
+                                       <td>
+                                               <a class="attendee-avatar" href="<?php echo esc_url( get_author_posts_url( $attendee->user_id() ) ); ?>" class="avatar"><?php echo get_avatar( $attendee->user_id(), 48 ); ?></a>
+                                               <a href="<?php echo esc_url( get_author_posts_url( $attendee->user_id() ) ); ?>" class="name"><?php echo esc_html( get_the_author_meta( 'display_name', $attendee->user_id() ) ); ?></a>
+                                       </td>
+                                       <td>
+                                               <?php if ( $attendee->is_host() ) : ?>
+                                                       <span><?php esc_html_e( 'Yes', 'gp-translation-events' ); ?></span>
+                                                       <?php endif; ?>
+                                       </td>
+                                       <td>
+                                       <form class="add-remove-user-as-host" method="post" action="<?php echo esc_url( Urls::event_toggle_host( $event->id(), $attendee->user_id() ) ); ?>">
+                                               <?php if ( $attendee->is_host() ) : ?>
+                                                       <input type="submit" class="button is-primary remove-as-host" value="<?php echo esc_attr__( 'Remove as host', 'gp-translation-events' ); ?>"/>
+                                                       <?php else : ?>
+                                                                       <input type="submit" class="button is-secondary convert-to-host" value="<?php echo esc_attr__( 'Make co-host', 'gp-translation-events' ); ?>"/>
+                                                       <?php endif; ?>
+                                               </form>
+                                       </td>
+                               </tr>
+                       <?php endforeach; ?>
+               </tbody>
+       </table>
+       <?php else : ?>
+               <p><?php esc_html_e( 'No attendees found.', 'gp-translation-events' ); ?></p>
+       </div>
+<?php endif; ?>
+<?php
+gp_tmpl_footer();
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-attendees.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventsformphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-form.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-form.php    2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-form.php      2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -28,21 +28,24 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <div class="event-page-wrapper">
</span><span class="cx" style="display: block; padding: 0 10px"> <form class="translation-event-form" action="" method="post">
</span><span class="cx" style="display: block; padding: 0 10px">        <?php wp_nonce_field( '_event_nonce', '_event_nonce' ); ?>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        <?php if ( ! $event_id ) : ?>
+               <details id="quick-add"><summary><?php esc_html_e( 'Upcoming WordCamps', 'gp-translation-events' ); ?></summary><div class="loading"></div></details>
+       <?php endif; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">         <input type="hidden" name="action" value="submit_event_ajax">
</span><span class="cx" style="display: block; padding: 0 10px">        <input type="hidden" id="form-name" name="form_name" value="<?php echo esc_attr( $event_form_name ); ?>">
</span><span class="cx" style="display: block; padding: 0 10px">        <input type="hidden" id="event-id" name="event_id" value="<?php echo esc_attr( $event_id ); ?>">
</span><span class="cx" style="display: block; padding: 0 10px">        <input type="hidden" id="event-form-action" name="event_form_action">
</span><span class="cx" style="display: block; padding: 0 10px">        <div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <label for="event-title">Event Title</label>
-               <input type="text" id="event-title" name="event_title" value="<?php echo esc_html( $event_title ); ?>" required>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <label for="event-title"><?php esc_html_e( 'Event Title', 'gp-translation-events' ); ?></label>
+               <input type="text" id="event-title" name="event_title" value="<?php echo esc_html( $event_title ); ?>" required size="42">
</ins><span class="cx" style="display: block; padding: 0 10px">         </div>
</span><span class="cx" style="display: block; padding: 0 10px">        <div id="event-url" class="<?php echo esc_attr( $css_show_url ); ?>">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <label for="event-permalink">Event URL</label>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <label for="event-permalink"><?php esc_html_e( 'Event URL', 'gp-translation-events' ); ?></label>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <a id="event-permalink" class="event-permalink" href="<?php echo esc_url( $event_url ); ?>" target="_blank"><?php echo esc_url( $event_url ); ?></a>
</span><span class="cx" style="display: block; padding: 0 10px">        </div>
</span><span class="cx" style="display: block; padding: 0 10px">        <div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <label for="event-description">Event Description</label>
-               <textarea id="event-description" name="event_description" rows="4" required><?php echo esc_html( $event_description ); ?></textarea>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <label for="event-description"><?php esc_html_e( 'Event Description', 'gp-translation-events' ); ?></label>
+               <textarea id="event-description" name="event_description" rows="4" cols="40" required><?php echo esc_html( $event_description ); ?></textarea>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <?php
</span><span class="cx" style="display: block; padding: 0 10px">                echo wp_kses(
</span><span class="cx" style="display: block; padding: 0 10px">                        Event_Text_Snippet::get_snippet_links(),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -58,15 +61,15 @@
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">                ?>
</span><span class="cx" style="display: block; padding: 0 10px">                        <div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <label for="event-start">Start Date</label>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <label for="event-start"><?php esc_html_e( 'Start Date', 'gp-translation-events' ); ?></label>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <input type="datetime-local" id="event-start" name="event_start" value="<?php echo esc_attr( $event_start->format( 'Y-m-d H:i' ) ); ?>" required>
</span><span class="cx" style="display: block; padding: 0 10px">        </div>
</span><span class="cx" style="display: block; padding: 0 10px">        <div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <label for="event-end">End Date</label>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <label for="event-end"><?php esc_html_e( 'End Date', 'gp-translation-events' ); ?></label>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <input type="datetime-local" id="event-end" name="event_end" value="<?php echo esc_attr( $event_end->format( 'Y-m-d H:i' ) ); ?>" required>
</span><span class="cx" style="display: block; padding: 0 10px">        </div>
</span><span class="cx" style="display: block; padding: 0 10px">        <div>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <label for="event-timezone">Event Timezone</label>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <label for="event-timezone"><?php esc_html_e( 'Event Timezone', 'gp-translation-events' ); ?></label>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <select id="event-timezone" name="event_timezone" required>
</span><span class="cx" style="display: block; padding: 0 10px">                        <?php
</span><span class="cx" style="display: block; padding: 0 10px">                        echo wp_kses(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -95,8 +98,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <button class="button is-primary save-draft submit-event" type="submit" data-event-status="draft">Save Draft</button>
</span><span class="cx" style="display: block; padding: 0 10px">                <button class="button is-primary submit-event" type="submit"  data-event-status="publish">Publish Event</button>
</span><span class="cx" style="display: block; padding: 0 10px">        <?php endif; ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <?php if ( isset( $create_delete_button ) && $create_delete_button ) : ?>
-               <button id="delete-button" class="button is-destructive delete-event" type="submit" name="submit" value="Delete" style="display: <?php echo esc_attr( $visibility_delete_button ); ?>">Delete Event</button>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <?php if ( isset( $create_trash_button ) && $create_trash_button ) : ?>
+               <button id="trash-button" class="button is-destructive trash-event" type="submit" name="submit" value="Delete" style="display: <?php echo esc_attr( $visibility_trash_button ); ?>">Delete Event</button>
</ins><span class="cx" style="display: block; padding: 0 10px">         <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px">        </div>
</span><span class="cx" style="display: block; padding: 0 10px">        <div class="clear"></div>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -126,5 +129,10 @@
</span><span class="cx" style="display: block; padding: 0 10px">        </div>
</span><span class="cx" style="display: block; padding: 0 10px"> </form>
</span><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php if ( $event_id ) : ?>
+       <div class="event-edit-right">
+               <a class="manage-attendees-btn button is-primary" href="<?php echo esc_url( Urls::event_attendees( $event_id ) ); ?>"><?php esc_html_e( 'Manage Attendees', 'gp-translation-events' ); ?></a>
+       </div>
+<?php endif; ?>
</ins><span class="cx" style="display: block; padding: 0 10px"> <div class="clear"></div>
</span><span class="cx" style="display: block; padding: 0 10px"> <?php gp_tmpl_footer(); ?>
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventsheaderphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-header.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-header.php  2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-header.php    2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -2,14 +2,15 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> namespace Wporg\TranslationEvents;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-use GP;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use WP_User;
</ins><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Attendee\Attendee;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Event\Event;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/** @var WP_User $user */
+/** @var Attendee[] $hosts */
</ins><span class="cx" style="display: block; padding: 0 10px"> /** @var Attendee $attendee */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/** @var Event  $event */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/** @var Event $event */
</ins><span class="cx" style="display: block; padding: 0 10px"> /** @var string $event_page_title */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/** @var bool   $is_editable_event */
</del><span class="cx" style="display: block; padding: 0 10px"> ?>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> <div class="event-list-top-bar">
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -21,49 +22,47 @@
</span><span class="cx" style="display: block; padding: 0 10px"> </h2>
</span><span class="cx" style="display: block; padding: 0 10px">        <ul class="event-list-nav">
</span><span class="cx" style="display: block; padding: 0 10px">                <?php if ( is_user_logged_in() ) : ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <li><a href="<?php echo esc_url( gp_url( '/events/my-events/' ) ); ?>">My Events</a></li>
-                       <?php
-                       /**
-                        * Filter the ability to create, edit, or delete an event.
-                        *
-                        * @param bool $can_crud_event Whether the user can create, edit, or delete an event.
-                        */
-                       $can_crud_event = apply_filters( 'gp_translation_events_can_crud_event', GP::$permission->current_user_can( 'admin' ) );
-                       if ( $can_crud_event ) :
-                               ?>
-                               <li><a class="button is-primary" href="<?php echo esc_url( gp_url( '/events/new/' ) ); ?>">Create Event</a></li>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <?php if ( current_user_can( 'manage_translation_events' ) ) : ?>
+                               <li><a href="<?php echo esc_url( Urls::events_trashed() ); ?>">Deleted Events</a></li>
</ins><span class="cx" style="display: block; padding: 0 10px">                         <?php endif; ?>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        <li><a href="<?php echo esc_url( Urls::my_events() ); ?>">My Events</a></li>
+                       <?php if ( current_user_can( 'create_translation_event' ) ) : ?>
+                               <li><a class="button is-primary" href="<?php echo esc_url( Urls::event_create() ); ?>">Create Event</a></li>
+                       <?php endif; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                 <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px">        </ul>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <?php if ( isset( $event ) && ! isset( $event_form_name ) ) : ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ <?php if ( isset( $event ) && ! isset( $event_form_name ) && ! isset( $hide_sub_head ) ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">         <p class="event-sub-head">
</span><span class="cx" style="display: block; padding: 0 10px">                        <span class="event-host">
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                if ( count( $hosts ) > 0 ) :
-                                       if ( 1 === count( $hosts ) ) :
-                                               esc_html_e( 'Host:', 'gp-translation-events' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         if ( isset( $hosts ) ) :
+                                       if ( count( $hosts ) > 0 ) :
+                                               if ( 1 === count( $hosts ) ) :
+                                                       esc_html_e( 'Host:', 'gp-translation-events' );
+                                               else :
+                                                       esc_html_e( 'Hosts:', 'gp-translation-events' );
+                                               endif;
</ins><span class="cx" style="display: block; padding: 0 10px">                                         else :
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                esc_html_e( 'Hosts:', 'gp-translation-events' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         esc_html_e( 'Created by:', 'gp-translation-events' );
+                                               ?>
+                                               &nbsp;<a href="<?php echo esc_attr( get_author_posts_url( $user->ID ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $user->ID ) ); ?></a>
+                                               <?php
</ins><span class="cx" style="display: block; padding: 0 10px">                                         endif;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                else :
-                                       esc_html_e( 'Created by:', 'gp-translation-events' );
</del><span class="cx" style="display: block; padding: 0 10px">                                         ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        &nbsp;<a href="<?php echo esc_attr( get_author_posts_url( $user->ID ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $user->ID ) ); ?></a>
-                                       <?php
-                               endif;
-                               ?>
-                               <?php foreach ( $hosts as $host ) : ?>
-                                       &nbsp;<a href="<?php echo esc_attr( get_author_posts_url( $host->user_id() ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $host->user_id() ) ); ?></a>
-                                       <?php if ( end( $hosts ) !== $host ) : ?>
-                                               ,
-                                       <?php else : ?>
-                                               .
-                                       <?php endif; ?>
-                               <?php endforeach; ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <?php foreach ( $hosts as $host ) : ?>
+                                               &nbsp;<a href="<?php echo esc_attr( get_author_posts_url( $host->user_id() ) ); ?>"><?php echo esc_html( get_the_author_meta( 'display_name', $host->user_id() ) ); ?></a>
+                                               <?php if ( end( $hosts ) !== $host ) : ?>
+                                                       ,
+                                               <?php else : ?>
+                                                       .
+                                               <?php endif; ?>
+                                       <?php endforeach; ?>
+                               <?php endif; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                         </span>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <?php $show_edit_button = ( ( $attendee instanceof Attendee && $attendee->is_host() ) || current_user_can( 'edit_post', $event->id() ) ) && $is_editable_event; ?>
-                       <?php if ( $show_edit_button ) : ?>
-                               <a class="event-page-edit-link" href="<?php echo esc_url( gp_url( 'events/edit/' . $event->id() ) ); ?>"><span class="dashicons dashicons-edit"></span><?php esc_html_e( 'Edit event', 'gp-translation-events' ); ?></a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 <?php if ( current_user_can( 'edit_translation_event', $event->id() ) ) : ?>
+                               <a class="event-page-edit-link" href="<?php echo esc_url( Urls::event_edit( $event->id() ) ); ?>"><span class="dashicons dashicons-edit"></span><?php esc_html_e( 'Edit event', 'gp-translation-events' ); ?></a>
+                       <?php elseif ( current_user_can( 'edit_translation_event_attendees', $event->id() ) ) : ?>
+                               <a class="event-page-attendees-link" href="<?php echo esc_url( Urls::event_attendees( $event->id() ) ); ?>"><?php esc_html_e( 'Manage attendees', 'gp-translation-events' ); ?></a>
</ins><span class="cx" style="display: block; padding: 0 10px">                         <?php endif ?>
</span><span class="cx" style="display: block; padding: 0 10px">                </p>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php endif; ?>
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventslisttrashedphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-list-trashed.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-list-trashed.php                            (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-list-trashed.php      2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,59 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+
+namespace Wporg\TranslationEvents;
+
+use Wporg\TranslationEvents\Event\Events_Query_Result;
+
+/** @var Events_Query_Result $trashed_events_query */
+
+gp_title( __( 'Deleted Translation Events', 'gp-translation-events' ) );
+gp_breadcrumb_translation_events();
+gp_tmpl_header();
+$event_page_title = __( 'Deleted Translation Events', 'gp-translation-events' );
+gp_tmpl_load( 'events-header', get_defined_vars(), __DIR__ );
+?>
+
+<div class="event-page-wrapper">
+       <div class="event-left-col">
+               <?php if ( empty( $trashed_events_query->events ) ) : ?>
+                       <?php esc_html_e( 'No deleted events found.', 'gp-translation-events' ); ?>
+               <?php else : ?>
+                       <ul class="event-list">
+                               <?php foreach ( $trashed_events_query->events as $event ) : ?>
+                                       <li class="event-list-item">
+                                               <a href="<?php echo esc_url( Urls::event_edit( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a>
+                                               <?php if ( current_user_can( 'trash_translation_event', $event->id() ) ) : ?>
+                                                       <a href="<?php echo esc_url( Urls::event_trash( $event->id() ) ); ?>" class="button is-small">Restore</a>
+                                               <?php endif; ?>
+                                               <?php if ( current_user_can( 'delete_translation_event', $event->id() ) ) : ?>
+                                                       <a href="<?php echo esc_url( Urls::event_delete( $event->id() ) ); ?>" class="button is-small is-destructive">Delete Permanently</a>
+                                               <?php endif; ?>
+                                               <?php if ( $event->is_past() ) : ?>
+                                                       <span class="event-list-date">ended <?php $event->end()->print_relative_time_html(); ?></time></span>
+                                               <?php else : ?>
+                                                       <span class="event-list-date">ends <?php $event->end()->print_relative_time_html(); ?></time></span>
+                                               <?php endif; ?>
+                                               <?php echo esc_html( get_the_excerpt( $event->id() ) ); ?>
+                                       </li>
+                               <?php endforeach; ?>
+                       </ul>
+
+                       <?php
+                       echo wp_kses_post(
+                               paginate_links(
+                                       array(
+                                               'total'     => $trashed_events_query->page_count,
+                                               'current'   => $trashed_events_query->current_page,
+                                               'format'    => '?page=%#%',
+                                               'prev_text' => '&laquo; Previous',
+                                               'next_text' => 'Next &raquo;',
+                                       )
+                               ) ?? ''
+                       );
+                       ?>
+               <?php endif; ?>
+       </div>
+</div>
+
+<div class="clear"></div>
+<?php gp_tmpl_footer(); ?>
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-list-trashed.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventslistphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-list.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-list.php    2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-list.php      2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -29,18 +29,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">        ?>
</span><span class="cx" style="display: block; padding: 0 10px">        <h2><?php esc_html_e( 'Current events', 'gp-translation-events' ); ?></h2>
</span><span class="cx" style="display: block; padding: 0 10px">        <ul class="event-list">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <?php
-               foreach ( $current_events_query->events as $event ) :
-                       $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) );
-                       ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php foreach ( $current_events_query->events as $event ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                         <li class="event-list-item">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <a href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <a href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 <span class="event-list-date">ends <?php $event->end()->print_relative_time_html(); ?></time></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php echo esc_html( get_the_excerpt( $event->id() ) ); ?>
</span><span class="cx" style="display: block; padding: 0 10px">                        </li>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <?php
-               endforeach;
-               ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php endforeach; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">         </ul>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        <?php
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -63,18 +58,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">        ?>
</span><span class="cx" style="display: block; padding: 0 10px">        <h2><?php esc_html_e( 'Upcoming events', 'gp-translation-events' ); ?></h2>
</span><span class="cx" style="display: block; padding: 0 10px">        <ul class="event-list">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <?php
-               foreach ( $upcoming_events_query->events as $event ) :
-                       $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) );
-                       ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php foreach ( $upcoming_events_query->events as $event ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                         <li class="event-list-item">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <a href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <a href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 <span class="event-list-date">starts <?php $event->start()->print_relative_time_html(); ?></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php echo esc_html( get_the_excerpt( $event->id() ) ); ?>
</span><span class="cx" style="display: block; padding: 0 10px">                        </li>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <?php
-               endforeach;
-               ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php endforeach; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">         </ul>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        <?php
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -96,18 +86,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">        ?>
</span><span class="cx" style="display: block; padding: 0 10px">        <h2><?php esc_html_e( 'Past events', 'gp-translation-events' ); ?></h2>
</span><span class="cx" style="display: block; padding: 0 10px">        <ul class="event-list">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <?php
-               foreach ( $past_events_query->events as $event ) :
-                       $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) );
-                       ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php foreach ( $past_events_query->events as $event ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                         <li class="event-list-item">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <a href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <a href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 <span class="event-list-date">ended <?php $event->end()->print_relative_time_html( 'F j, Y H:i T' ); ?></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php esc_html( get_the_excerpt( $event->id() ) ); ?>
</span><span class="cx" style="display: block; padding: 0 10px">                        </li>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        <?php
-               endforeach;
-               ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php endforeach; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">         </ul>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        <?php
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -138,12 +123,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        <p>You don't have any events to attend.</p>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php else : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                        <ul class="event-attending-list">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <?php
-                               foreach ( $user_attending_events_query->events as $event ) :
-                                       $event_url = gp_url( wp_make_link_relative( get_the_permalink( $event->id() ) ) );
-                                       ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <?php foreach ( $user_attending_events_query->events as $event ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                                         <li class="event-list-item">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                <a href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         <a href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</ins><span class="cx" style="display: block; padding: 0 10px">                                                 <?php if ( $event->start() === $event->end() ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                                        <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html( 'F j, Y H:i T' ); ?></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                                <?php else : ?>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -150,9 +132,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                                        <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html( 'F j, Y H:i T' ); ?> - <?php $event->end()->print_time_html( 'F j, Y H:i T' ); ?></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                                <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                        </li>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        <?php
-                               endforeach;
-                               ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <?php endforeach; ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                         </ul>
</span><span class="cx" style="display: block; padding: 0 10px">                        <?php
</span><span class="cx" style="display: block; padding: 0 10px">                                echo wp_kses_post(
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateseventsmyeventsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-my-events.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-my-events.php       2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/events-my-events.php 2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -25,17 +25,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <ul>
</span><span class="cx" style="display: block; padding: 0 10px">                <?php
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( $events_i_host_query->events as $event ) :
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        list( $permalink, $post_name ) = get_sample_permalink( $event->id() );
-                       $permalink                     = str_replace( '%pagename%', $post_name, $permalink );
-                       $event_url                     = gp_url( wp_make_link_relative( $permalink ) );
-                       $event_edit_url                = gp_url( 'events/edit/' . $event->id() );
-                       $stats_calculator              = new Stats_Calculator();
-                       $has_stats                     = $stats_calculator->event_has_stats( $event->id() );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $stats_calculator = new Stats_Calculator();
+                       $has_stats        = $stats_calculator->event_has_stats( $event->id() );
</ins><span class="cx" style="display: block; padding: 0 10px">                         ?>
</span><span class="cx" style="display: block; padding: 0 10px">                        <li class="event-list-item">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 <?php if ( ! $event->end()->is_in_the_past() && ! $has_stats ) : ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        <a href="<?php echo esc_url( $event_edit_url ); ?>" class="button is-small action edit">Edit</a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <a href="<?php echo esc_url( Urls::event_edit( $event->id() ) ); ?>" class="button is-small action edit">Edit</a>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php if ( 'draft' === $event->status() ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <span class="event-label-<?php echo esc_attr( $event->status() ); ?>"><?php echo esc_html( $event->status() ); ?></span>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -72,17 +68,13 @@
</span><span class="cx" style="display: block; padding: 0 10px">                <ul>
</span><span class="cx" style="display: block; padding: 0 10px">                        <?php
</span><span class="cx" style="display: block; padding: 0 10px">                        foreach ( $events_i_created_query->events as $event ) :
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                list( $permalink, $post_name ) = get_sample_permalink( $event->id() );
-                               $permalink                     = str_replace( '%pagename%', $post_name, $permalink );
-                               $event_url                     = gp_url( wp_make_link_relative( $permalink ) );
-                               $event_edit_url                = gp_url( 'events/edit/' . $event->id() );
-                               $stats_calculator              = new Stats_Calculator();
-                               $has_stats                     = $stats_calculator->event_has_stats( $event->id() );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $stats_calculator = new Stats_Calculator();
+                               $has_stats        = $stats_calculator->event_has_stats( $event->id() );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                <li class="event-list-item">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                        <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                 <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</ins><span class="cx" style="display: block; padding: 0 10px">                                         <?php if ( ! $event->end()->is_in_the_past() && ! $has_stats ) : ?>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                                <a href="<?php echo esc_url( $event_edit_url ); ?>" class="button is-small action edit">Edit</a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                                         <a href="<?php echo esc_url( Urls::event_edit( $event->id() ) ); ?>" class="button is-small action edit">Edit</a>
</ins><span class="cx" style="display: block; padding: 0 10px">                                         <?php endif; ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <?php if ( 'draft' === $event->status() ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                                <span class="event-label-<?php echo esc_attr( $event->status() ); ?>"><?php echo esc_html( $event->status() ); ?></span>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -117,14 +109,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">        <h2><?php esc_html_e( 'Events I attended', 'gp-translation-events' ); ?> </h2>
</span><span class="cx" style="display: block; padding: 0 10px">        <?php if ( ! empty( $events_i_attended_query->events ) ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                <ul>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <?php
-               foreach ( $events_i_attended_query->events as $event ) :
-                       list( $permalink, $post_name ) = get_sample_permalink( $event->id() );
-                       $permalink                     = str_replace( '%pagename%', $post_name, $permalink );
-                       $event_url                     = gp_url( wp_make_link_relative( $permalink ) );
-                       ?>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         <?php foreach ( $events_i_attended_query->events as $event ) : ?>
</ins><span class="cx" style="display: block; padding: 0 10px">                         <li class="event-list-item">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( $event_url ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         <a class="event-link-<?php echo esc_attr( $event->status() ); ?>" href="<?php echo esc_url( Urls::event_details( $event->id() ) ); ?>"><?php echo esc_html( $event->title() ); ?></a>
</ins><span class="cx" style="display: block; padding: 0 10px">                                 <?php if ( $event->start() === $event->end() ) : ?>
</span><span class="cx" style="display: block; padding: 0 10px">                                        <span class="event-list-date events-i-am-attending"><?php $event->start()->print_time_html(); ?></span>
</span><span class="cx" style="display: block; padding: 0 10px">                                <?php else : ?>
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplateshelperfunctionsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/helper-functions.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/helper-functions.php       2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/helper-functions.php 2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,4 +1,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> <?php
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+use Wporg\TranslationEvents\Urls;
+
</ins><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="cx" style="display: block; padding: 0 10px">  * Get event breadcrumb.
</span><span class="cx" style="display: block; padding: 0 10px">  *
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -8,7 +11,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="cx" style="display: block; padding: 0 10px"> function gp_breadcrumb_translation_events( $extra_items = array() ) {
</span><span class="cx" style="display: block; padding: 0 10px">        $breadcrumb = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                empty( $extra_items ) ? __( 'Events', 'gp-translation-events' ) : gp_link_get( gp_url( '/events' ), __( 'Events', 'gp-translation-events' ) ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         empty( $extra_items ) ? __( 'Events', 'gp-translation-events' ) : gp_link_get( Urls::events_home(), __( 'Events', 'gp-translation-events' ) ),
</ins><span class="cx" style="display: block; padding: 0 10px">         );
</span><span class="cx" style="display: block; padding: 0 10px">        if ( ! empty( $extra_items ) ) {
</span><span class="cx" style="display: block; padding: 0 10px">                $breadcrumb = array_merge( $breadcrumb, $extra_items );
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventstemplatestranslationsphp"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/translations.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/translations.php                           (rev 0)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/translations.php     2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,153 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+namespace Wporg\TranslationEvents;
+
+use GP;
+use Wporg\TranslationEvents\Event\Event;
+
+/** @var Event  $event */
+?>
+<div id="translations_<?php echo esc_attr( $translation_set->id ); ?>">
+<div class="gp-heading">
+       <h3>
+               <?php
+               printf(
+                       /* translators: 1: Project name. 2: Translation set name. */
+                       esc_html__( 'Translation of %1$s: %2$s', 'glotpress' ),
+                       wp_kses(
+                               gp_link_get(
+                                       gp_url_project_locale( $project, $translation_set->locale, $translation_set->slug ),
+                                       esc_html(
+                                               gp_project_names_from_root( $project )
+                                       )
+                               ),
+                               array(
+                                       'a' => array(
+                                               'href'  => array(),
+                                               'title' => array(),
+                                       ),
+                               )
+                       ),
+                       esc_html( $locale->name )
+               );
+               ?>
+       </h3>
+</div>
+<div class="gp-table-actions top">
+       <?php
+       if ( $can_approve ) {
+               gp_translations_bulk_actions_toolbar( $bulk_action, $can_write, $translation_set, 'top' );
+       }
+       ?>
+</div>
+
+<?php $class_rtl = 'rtl' === $locale->text_direction ? ' translation-sets-rtl' : ''; ?>
+<?php
+/**
+ * Fires before the translation table has been displayed.
+ *
+ * @since 4.0.0
+ *
+ * @param array $def_vars Variables defined in the template.
+ */
+do_action( 'gp_before_translation_table', get_defined_vars() );
+?>
+<table data-translation-set="<?php echo esc_attr( $translation_set->id ); ?>" class="<?php echo esc_attr( apply_filters( 'gp_translation_table_classes', 'gp-table translations ' . $class_rtl, get_defined_vars() ) ); ?>">
+       <thead>
+       <tr>
+               <?php
+               if ( $can_approve ) :
+                       ?>
+                       <th class="gp-column-checkbox checkbox" scope="row"><input type="checkbox" /></th>
+                       <?php
+               endif;
+               ?>
+               <th class="gp-column-priority"><?php /* Translators: Priority */ esc_html_e( 'Prio', 'glotpress' ); ?></th>
+               <th class="gp-column-original"><?php esc_html_e( 'Original string', 'glotpress' ); ?></th>
+               <th class="gp-column-translation"><?php esc_html_e( 'Translation', 'glotpress' ); ?></th>
+               <th class="gp-column-actions">&mdash;</th>
+       </tr>
+       </thead>
+<?php
+foreach ( $translations as $translation ) {
+       if ( ! $translation->translation_set_id ) {
+               $translation->translation_set_id = $translation_set->id;
+       }
+
+       $can_approve_translation = GP::$permission->current_user_can( 'approve', 'translation', $translation->id, array( 'translation' => $translation ) );
+       gp_tmpl_load( 'translation-row', get_defined_vars() );
+}
+?>
+<tr class="preview" style="display: none"></tr>
+<?php
+if ( ! $translations ) :
+       ?>
+       <tr><td colspan="<?php echo $can_approve ? 5 : 4; ?>"><?php esc_html_e( 'No translations were found!', 'glotpress' ); ?></td></tr>
+       <?php
+       endif;
+?>
+</table>
+<?php
+/**
+ * Fires after the translation table has been displayed.
+ *
+ * @since 4.0.0
+ *
+ * @param array $def_vars Variables defined in the template.
+ */
+do_action( 'gp_after_translation_table', get_defined_vars() );
+?>
+
+<div class="gp-table-actions bottom">
+       <?php
+       if ( $can_approve ) {
+               gp_translations_bulk_actions_toolbar( $bulk_action, $can_write, $translation_set, 'bottom' );
+       }
+       ?>
+       <div id="legend">
+               <div><strong><?php esc_html_e( 'Legend:', 'glotpress' ); ?></strong></div>
+               <?php
+               foreach ( GP::$translation->get_static( 'statuses' ) as $legend_status ) :
+                       if ( ( 'changesrequested' === $legend_status ) && ( ! apply_filters( 'gp_enable_changesrequested_status', false ) ) ) { // todo: delete when we merge the gp-translation-helpers in GlotPress.
+                               continue;
+                       }
+                       ?>
+                       <div class="box status-<?php echo esc_attr( $legend_status ); ?>"></div>
+                       <div>
+                               <?php
+                               switch ( $legend_status ) {
+                                       case 'current':
+                                               esc_html_e( 'Current', 'glotpress' );
+                                               break;
+                                       case 'waiting':
+                                               esc_html_e( 'Waiting', 'glotpress' );
+                                               break;
+                                       case 'fuzzy':
+                                               esc_html_e( 'Fuzzy', 'glotpress' );
+                                               break;
+                                       case 'old':
+                                               esc_html_e( 'Old', 'glotpress' );
+                                               break;
+                                       case 'rejected':
+                                               esc_html_e( 'Rejected', 'glotpress' );
+                                               break;
+                                       case 'changesrequested':
+                                               if ( apply_filters( 'gp_enable_changesrequested_status', false ) ) { // todo: delete when we merge the gp-translation-helpers in GlotPress.
+                                                       esc_html_e( 'Changes requested', 'glotpress' );
+                                               } else {
+                                                       esc_html_e( 'Rejected', 'glotpress' );
+                                               }
+                                               break;
+                                       default:
+                                               echo esc_html( $legend_status );
+                               }
+                               ?>
+                       </div>
+                       <?php
+               endforeach;
+               ?>
+               <div class="box has-warnings"></div>
+               <div><?php esc_html_e( 'With warnings', 'glotpress' ); ?></div>
+       </div>
+</div>
+</div>
+<hr>
</ins><span class="cx" style="display: block; padding: 0 10px">Property changes on: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/templates/translations.php
</span><span class="cx" style="display: block; padding: 0 10px">___________________________________________________________________
</span></span></pre></div>
<a id="svneolstyle"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: svn:eol-style</h4></div>
<ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+native
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of property
</span><a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginswporggptranslationeventswporggptranslationeventsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/wporg-gp-translation-events.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/wporg-gp-translation-events.php      2024-05-09 05:04:06 UTC (rev 13682)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/wporg-gp-translation-events/wporg-gp-translation-events.php        2024-05-09 08:33:51 UTC (rev 13683)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -22,18 +22,24 @@
</span><span class="cx" style="display: block; padding: 0 10px"> use DateTimeZone;
</span><span class="cx" style="display: block; padding: 0 10px"> use Exception;
</span><span class="cx" style="display: block; padding: 0 10px"> use GP;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use GP_Locales;
</ins><span class="cx" style="display: block; padding: 0 10px"> use WP_Post;
</span><span class="cx" style="display: block; padding: 0 10px"> use WP_Query;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Attendee\Attendee;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Attendee\Attendee_Repository;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use Wporg\TranslationEvents\Event\Event_Capabilities;
</ins><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Event\Event_Form_Handler;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Event\Event_Repository_Cached;
</span><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Event\Event_Repository_Interface;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+use Wporg\TranslationEvents\Notifications\Notifications_Send;
+use Wporg\TranslationEvents\Stats\Stats_Calculator;
</ins><span class="cx" style="display: block; padding: 0 10px"> use Wporg\TranslationEvents\Stats\Stats_Listener;
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px"> class Translation_Events {
</span><span class="cx" style="display: block; padding: 0 10px">        public const CPT = 'translation_event';
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        private Event_Capabilities $event_capabilities;
+
</ins><span class="cx" style="display: block; padding: 0 10px">         public static function get_instance(): Translation_Events {
</span><span class="cx" style="display: block; padding: 0 10px">                static $instance = null;
</span><span class="cx" style="display: block; padding: 0 10px">                if ( null === $instance ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -64,6 +70,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'wp_ajax_nopriv_submit_event_ajax', array( $this, 'submit_event_ajax' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'wp_enqueue_scripts', array( $this, 'register_translation_event_js' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'init', array( $this, 'register_event_post_type' ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                add_action( 'init', array( $this, 'send_notifications' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 add_action( 'add_meta_boxes', array( $this, 'event_meta_boxes' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'save_post', array( $this, 'save_event_meta_boxes' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'transition_post_status', array( $this, 'event_status_transition' ), 10, 3 );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -71,20 +78,40 @@
</span><span class="cx" style="display: block; padding: 0 10px">                add_filter( 'wp_insert_post_data', array( $this, 'generate_event_slug' ), 10, 2 );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'gp_init', array( $this, 'gp_init' ) );
</span><span class="cx" style="display: block; padding: 0 10px">                add_action( 'gp_before_translation_table', array( $this, 'add_active_events_current_user' ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                add_filter( 'wp_post_revision_meta_keys', array( $this, 'wp_post_revision_meta_keys' ) );
+               add_filter( 'pre_wp_unique_post_slug', array( $this, 'pre_wp_unique_post_slug' ), 10, 6 );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( is_admin() ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        Upgrade::upgrade_if_needed();
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               $this->event_capabilities = new Event_Capabilities(
+                       self::get_event_repository(),
+                       self::get_attendee_repository(),
+                       new Stats_Calculator()
+               );
+               $this->event_capabilities->register_hooks();
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        public function gp_init() {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $locale = '(' . implode( '|', wp_list_pluck( GP_Locales::locales(), 'slug' ) ) . ')';
+               $slug   = '((?:2[0-9]{3}/)?[a-z0-9_-]+)';
+               $status = '(waiting)';
+               $id     = '(\d+)';
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 GP::$router->add( '/events?', array( 'Wporg\TranslationEvents\Routes\Event\List_Route', 'handle' ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                GP::$router->add( '/events/trashed?', array( 'Wporg\TranslationEvents\Routes\Event\List_Trashed_Route', 'handle' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">                 GP::$router->add( '/events/new', array( 'Wporg\TranslationEvents\Routes\Event\Create_Route', 'handle' ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                GP::$router->add( '/events/edit/(\d+)', array( 'Wporg\TranslationEvents\Routes\Event\Edit_Route', 'handle' ) );
-               GP::$router->add( '/events/attend/(\d+)', array( 'Wporg\TranslationEvents\Routes\User\Attend_Event_Route', 'handle' ), 'post' );
-               GP::$router->add( '/events/host/(\d+)/(\d+)', array( 'Wporg\TranslationEvents\Routes\User\Host_Event_Route', 'handle' ), 'post' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         GP::$router->add( "/events/edit/$id", array( 'Wporg\TranslationEvents\Routes\Event\Edit_Route', 'handle' ) );
+               GP::$router->add( "/events/trash/$id", array( 'Wporg\TranslationEvents\Routes\Event\Trash_Route', 'handle' ) );
+               GP::$router->add( "/events/delete/$id", array( 'Wporg\TranslationEvents\Routes\Event\Delete_Route', 'handle' ) );
+               GP::$router->add( "/events/attend/$id", array( 'Wporg\TranslationEvents\Routes\User\Attend_Event_Route', 'handle' ), 'post' );
+               GP::$router->add( "/events/host/$id/$id", array( 'Wporg\TranslationEvents\Routes\User\Host_Event_Route', 'handle' ), 'post' );
</ins><span class="cx" style="display: block; padding: 0 10px">                 GP::$router->add( '/events/my-events', array( 'Wporg\TranslationEvents\Routes\User\My_Events_Route', 'handle' ) );
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                GP::$router->add( '/events/([a-z0-9_-]+)', array( 'Wporg\TranslationEvents\Routes\Event\Details_Route', 'handle' ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         GP::$router->add( "/events/$slug/translations/$locale/$status", array( 'Wporg\TranslationEvents\Routes\Event\Translations_Route', 'handle' ) );
+               GP::$router->add( "/events/$slug/translations/$locale", array( 'Wporg\TranslationEvents\Routes\Event\Translations_Route', 'handle' ) );
+               GP::$router->add( "/events/$slug", array( 'Wporg\TranslationEvents\Routes\Event\Details_Route', 'handle' ) );
+               GP::$router->add( "/events/$slug/attendees", array( 'Wporg\TranslationEvents\Routes\Attendee\List_Route', 'handle' ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $stats_listener = new Stats_Listener(
</span><span class="cx" style="display: block; padding: 0 10px">                        self::get_event_repository(),
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -112,13 +139,14 @@
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $args = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'labels'      => $labels,
-                       'public'      => true,
-                       'has_archive' => true,
-                       'menu_icon'   => 'dashicons-calendar',
-                       'supports'    => array( 'title', 'editor', 'thumbnail', 'revisions' ),
-                       'rewrite'     => array( 'slug' => 'events' ),
-                       'show_ui'     => false,
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'labels'       => $labels,
+                       'public'       => true,
+                       'has_archive'  => true,
+                       'hierarchical' => true,
+                       'menu_icon'    => 'dashicons-calendar',
+                       'supports'     => array( 'title', 'editor', 'thumbnail', 'revisions' ),
+                       'rewrite'      => array( 'slug' => 'events' ),
+                       'show_ui'      => false,
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                register_post_type( self::CPT, $args );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -162,7 +190,6 @@
</span><span class="cx" style="display: block; padding: 0 10px">                                return;
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-
</del><span class="cx" style="display: block; padding: 0 10px">                 $fields = array( 'event_start', 'event_end' );
</span><span class="cx" style="display: block; padding: 0 10px">                foreach ( $fields as $field ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( isset( $_POST[ $field ] ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -218,8 +245,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        $attendee            = $attendee_repository->get_attendee( $event_id, $user_id );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                        if ( null === $attendee ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                                $attendee = new Attendee( $event_id, $user_id );
-                               $attendee->mark_as_host();
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                         $attendee = new Attendee( $event_id, $user_id, true );
</ins><span class="cx" style="display: block; padding: 0 10px">                                 $attendee_repository->insert_attendee( $attendee );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -236,7 +262,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                if ( 'main' !== $location ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        return $items;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $new[ esc_url( gp_url( '/events/' ) ) ] = esc_html__( 'Events', 'gp-translation-events' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $new[ esc_url( Urls::events_home() ) ] = esc_html__( 'Events', 'gp-translation-events' );
</ins><span class="cx" style="display: block; padding: 0 10px">                 return array_merge( $items, $new );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -253,6 +279,9 @@
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function generate_event_slug( array $data, array $postarr ): array {
</span><span class="cx" style="display: block; padding: 0 10px">                if ( self::CPT === $data['post_type'] ) {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                        if ( isset( $data['post_name'] ) && preg_match( '/^2[0-9]+$/', $data['post_name'] ) ) {
+                               return $data;
+                       }
</ins><span class="cx" style="display: block; padding: 0 10px">                         if ( 'draft' === $data['post_status'] ) {
</span><span class="cx" style="display: block; padding: 0 10px">                                $data['post_name'] = sanitize_title( $data['post_title'] );
</span><span class="cx" style="display: block; padding: 0 10px">                        }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -277,40 +306,11 @@
</span><span class="cx" style="display: block; padding: 0 10px">         * @throws Exception
</span><span class="cx" style="display: block; padding: 0 10px">         */
</span><span class="cx" style="display: block; padding: 0 10px">        public function add_active_events_current_user(): void {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $attendee_repository      = new Attendee_Repository();
-               $user_attending_event_ids = $attendee_repository->get_events_for_user( get_current_user_id() );
-               if ( empty( $user_attending_event_ids ) ) {
-                       return;
-               }
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $event_repository            = self::get_event_repository();
+               $user_attending_events_query = $event_repository->get_current_events_for_user( get_current_user_id() );
+               $events                      = $user_attending_events_query->events;
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $current_datetime_utc       = ( new DateTime( 'now', new DateTimeZone( 'UTC' ) ) )->format( 'Y-m-d H:i:s' );
-               $user_attending_events_args = array(
-                       'post_type'   => self::CPT,
-                       'post__in'    => $user_attending_event_ids,
-                       'post_status' => 'publish',
-                       // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
-                       'meta_query'  => array(
-                               array(
-                                       'key'     => '_event_start',
-                                       'value'   => $current_datetime_utc,
-                                       'compare' => '<=',
-                                       'type'    => 'DATETIME',
-                               ),
-                               array(
-                                       'key'     => '_event_end',
-                                       'value'   => $current_datetime_utc,
-                                       'compare' => '>=',
-                                       'type'    => 'DATETIME',
-                               ),
-                       ),
-                       // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
-                       'meta_key'    => '_event_start',
-                       'orderby'     => 'meta_value',
-                       'order'       => 'ASC',
-               );
-
-               $user_attending_events_query = new WP_Query( $user_attending_events_args );
-               $number_of_events            = $user_attending_events_query->post_count;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $number_of_events = count( $events );
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( 0 === $number_of_events ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        return;
</span><span class="cx" style="display: block; padding: 0 10px">                }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -319,27 +319,17 @@
</span><span class="cx" style="display: block; padding: 0 10px">                /* translators: %d: Number of events */
</span><span class="cx" style="display: block; padding: 0 10px">                $content .= sprintf( _n( 'Contributing to %d event:', 'Contributing to %d events:', $number_of_events, 'gp-translation-events' ), $number_of_events );
</span><span class="cx" style="display: block; padding: 0 10px">                $content .= '&nbsp;&nbsp;';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+               foreach ( array_splice( $events, 0, 2 ) as $event ) {
+                       $content .= '<span class="active-events-before-translation-table"><a href="' . Urls::event_details( $event->id() ) . '" target="_blank">' . esc_html( $event->title() ) . '</a></span>';
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 if ( $number_of_events > 3 ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $counter = 0;
-                       while ( $user_attending_events_query->have_posts() && $counter < 2 ) {
-                               $user_attending_events_query->the_post();
-                               $url      = esc_url( gp_url( '/events/' . get_post_field( 'post_name', get_post() ) ) );
-                               $content .= '<span class="active-events-before-translation-table"><a href="' . $url . '" target="_blank">' . get_the_title() . '</a></span>';
-                               ++$counter;
-                       }
-
</del><span class="cx" style="display: block; padding: 0 10px">                         $remaining_events = $number_of_events - 2;
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $url              = esc_url( gp_url( '/events/' ) );
</del><span class="cx" style="display: block; padding: 0 10px">                         /* translators: %d: Number of remaining events */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        $content .= '<span class="remaining-events"><a href="' . $url . '" target="_blank">' . sprintf( esc_html__( ' and %d more events.', 'gp-translation-events' ), $remaining_events ) . '</a></span>';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 $content .= '<span class="remaining-events"><a href="' . esc_url( Urls::events_home() ) . '" target="_blank">' . sprintf( esc_html__( ' and %d more events.', 'gp-translation-events' ), $remaining_events ) . '</a></span>';
+               }
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                } else {
-                       while ( $user_attending_events_query->have_posts() ) {
-                               $user_attending_events_query->the_post();
-                               $url      = esc_url( gp_url( '/events/' . get_post_field( 'post_name', get_post() ) ) );
-                               $content .= '<span class="active-events-before-translation-table"><a href="' . $url . '" target="_blank">' . get_the_title() . '</a></span>';
-                       }
-               }
</del><span class="cx" style="display: block; padding: 0 10px">                 $content .= '</div>';
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                echo wp_kses(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -359,5 +349,35 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        )
</span><span class="cx" style="display: block; padding: 0 10px">                );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        * Send notifications for the events.
+        */
+       public function send_notifications() {
+               new Notifications_Send( self::get_event_repository(), self::get_attendee_repository() );
+       }
+
+       /**
+        * Add the event meta keys to the list of meta keys to keep in post revisions.
+        *
+        * @param array $keys The list of meta keys to keep in post revisions.
+        *
+        * @return array The modified list of meta keys to keep in post revisions.
+        */
+       public function wp_post_revision_meta_keys( array $keys ): array {
+               $meta_keys_to_keep = array( '_event_start', '_event_end', '_event_timezone', '_hosts' );
+               return array_merge( $keys, $meta_keys_to_keep );
+       }
+
+       public function pre_wp_unique_post_slug( $override_slug, string $slug, int $post_id, string $post_status, string $post_type, int $post_parent ) {
+               if ( self::CPT !== $post_type || $post_parent ) {
+                       return $override_slug;
+               }
+               // Normally the slug is not allowed to be a year, this overrides it since we have a CPT and translate.wordpress.org doesn't have blog posts.
+               if ( preg_match( '/^2[0-9]{3}$/', $slug ) ) {
+                       return $slug;
+               }
+               return $override_slug;
+       }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> Translation_Events::get_instance();
</span></span></pre>
</div>
</div>

</body>
</html>