<!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>[11921] sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers: Translate: Introduce notification on rejection and opt-in/opt-out in a thread</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/11921">11921</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/11921","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>2022-06-21 14:15:05 +0000 (Tue, 21 Jun 2022)</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: Introduce notification on rejection and opt-in/opt-out in a thread
See:
- https://github.com/GlotPress/gp-translation-helpers/pull/55
- https://github.com/GlotPress/gp-translation-helpers/pull/61
props akirk, spiraltee</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpershelpershelpertranslationdiscussionphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/helpers/helper-translation-discussion.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpershelpersassetscsstranslationdiscussioncss">sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/helpers-assets/css/translation-discussion.css</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpershelpersassetstemplatestranslationdiscussioncommentsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/helpers-assets/templates/translation-discussion-comments.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpersincludesclassgpnotificationsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/includes/class-gp-notifications.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpersincludesclassgptranslationhelpersphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/includes/class-gp-translation-helpers.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpersincludesclasswporgnotificationsphp">sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/includes/class-wporg-notifications.php</a></li>
<li><a href="#sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpersjstranslationhelpersjs">sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/js/translation-helpers.js</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpershelpershelpertranslationdiscussionphp"></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/gp-translation-helpers/helpers/helper-translation-discussion.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/gp-translation-helpers/helpers/helper-translation-discussion.php 2022-06-16 02:15:44 UTC (rev 11920)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/helpers/helper-translation-discussion.php 2022-06-21 14:15:05 UTC (rev 11921)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -278,12 +278,12 @@
</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"> // We can't do much if the comment was posted logged out.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( empty( $commentdata['comment_author'] ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( empty( $commentdata['user_id'] ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> return $approved;
</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"> // If our user has already contributed translations, approve comment.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $user_current_translations = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->gp_translations WHERE user_id = %s AND status = 'current'", $commentdata['comment_author'] ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $user_current_translations = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->gp_translations WHERE user_id = %s AND status = 'current'", $commentdata['user_id'] ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( $user_current_translations ) {
</span><span class="cx" style="display: block; padding: 0 10px"> $approved = true;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpershelpersassetscsstranslationdiscussioncss"></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/gp-translation-helpers/helpers-assets/css/translation-discussion.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/gp-translation-helpers/helpers-assets/css/translation-discussion.css 2022-06-16 02:15:44 UTC (rev 11920)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/helpers-assets/css/translation-discussion.css 2022-06-21 14:15:05 UTC (rev 11921)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -82,7 +82,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> .comment-content {
</span><span class="cx" style="display: block; padding: 0 10px"> text-align: start;
</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"> .alignright {
</span><span class="cx" style="display: block; padding: 0 10px"> float: right;
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+}
+.optin-message-for-each-discussion {
+ padding: 0.5rem 1rem;
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpershelpersassetstemplatestranslationdiscussioncommentsphp"></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/gp-translation-helpers/helpers-assets/templates/translation-discussion-comments.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/gp-translation-helpers/helpers-assets/templates/translation-discussion-comments.php 2022-06-16 02:15:44 UTC (rev 11920)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/helpers-assets/templates/translation-discussion-comments.php 2022-06-21 14:15:05 UTC (rev 11921)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -114,6 +114,19 @@
</span><span class="cx" style="display: block; padding: 0 10px"> ),
</span><span class="cx" style="display: block; padding: 0 10px"> $post_obj
</span><span class="cx" style="display: block; padding: 0 10px"> );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ echo '<div class="optin-message-for-each-discussion">';
+ echo wp_kses(
+ GP_Notifications::optin_message_for_each_discussion( $original_id ),
+ array(
+ 'a' => array(
+ 'href' => array(),
+ 'class' => array(),
+ 'data-original-id' => array(),
+ 'data-opt-type' => array(),
+ ),
+ )
+ );
+ echo '</div>';
</ins><span class="cx" style="display: block; padding: 0 10px"> } else {
</span><span class="cx" style="display: block; padding: 0 10px"> /* translators: Log in URL. */
</span><span class="cx" style="display: block; padding: 0 10px"> echo sprintf( __( 'You have to be <a href="%s">logged in</a> to comment.' ), esc_html( wp_login_url() ) );
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpersincludesclassgpnotificationsphp"></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/gp-translation-helpers/includes/class-gp-notifications.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/gp-translation-helpers/includes/class-gp-notifications.php 2022-06-16 02:15:44 UTC (rev 11920)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/includes/class-gp-notifications.php 2022-06-21 14:15:05 UTC (rev 11921)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -9,6 +9,13 @@
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> class GP_Notifications {
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Stores the related comments to the first one when the validator makes a bulk rejection.
+ *
+ * @since 0.0.2
+ * @var array
+ */
+ private static array $related_comments = array();
+ /**
</ins><span class="cx" style="display: block; padding: 0 10px"> * Sends notifications when a new comment in the discussion is stored using the WP REST API.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -27,6 +34,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> if ( ( '0' !== $comment->comment_parent ) ) { // Notify to the thread only if the comment is in a thread.
</span><span class="cx" style="display: block; padding: 0 10px"> self::send_emails_to_thread_commenters( $comment, $comment_meta );
</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 ( ( '0' === $comment->comment_parent ) && array_key_exists( 'reject_reason', $comment_meta ) && ( ! empty( $comment_meta['reject_reason'] ) ) ) { // Notify a rejection without parent comments.
+ self::send_rejection_email_to_translator( $comment, $comment_meta );
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> $root_comment = self::get_root_comment_in_a_thread( $comment );
</span><span class="cx" style="display: block; padding: 0 10px"> $root_comment_meta = get_comment_meta( $root_comment->comment_ID );
</span><span class="cx" style="display: block; padding: 0 10px"> if ( array_key_exists( 'comment_topic', $root_comment_meta ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -93,6 +103,26 @@
</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">+ * Sends the reject notification to the translator.
+ *
+ * @since 0.0.2
+ *
+ * @param WP_Comment $comment The comment object.
+ * @param array $comment_meta The meta values for the comment.
+ *
+ * @return void
+ */
+ public static function send_rejection_email_to_translator( WP_Comment $comment, array $comment_meta ) {
+ $translation_id = $comment_meta['translation_id'];
+ $translation = GP::$translation->get( $translation_id );
+ $translator = get_user_by( 'id', $translation->user_id_last_modified );
+ if ( false === $translator ) {
+ $translator = get_user_by( 'id', $translation->user_id );
+ }
+ self::send_emails( $comment, $comment_meta, array( $translator->user_email ) );
+ }
+
+ /**
</ins><span class="cx" style="display: block; padding: 0 10px"> * Sends an email to the GlotPress admins.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -118,7 +148,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * @return bool Whether the email has been sent or not.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> public static function send_emails_to_validators( WP_Comment $comment, array $comment_meta ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $project = self::get_project_to_translate( $comment );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $post = get_post( $comment->comment_post_ID );
+ $project = self::get_project_from_post( $post );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $email_addresses = self::get_validators_email_addresses( $project->path );
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -170,8 +201,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param array $comments Array with the parent comments to the posted comment.
- * @param string $email_address_to_ignore Email from the posted comment.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param array $comments Array with the parent comments to the posted comment.
+ * @param string|null $email_address_to_ignore Email from the posted comment.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @return array The emails to be notified from the thread comments.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -251,13 +282,16 @@
</span><span class="cx" style="display: block; padding: 0 10px"> return $email_addresses;
</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">- $admin_email_addresses = $wpdb->get_results(
- "SELECT user_email FROM {$wpdb->users}
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ try {
+ $admin_email_addresses = $wpdb->get_results(
+ "SELECT user_email FROM {$wpdb->users}
</ins><span class="cx" style="display: block; padding: 0 10px"> INNER JOIN {$wpdb->gp_permissions}
</span><span class="cx" style="display: block; padding: 0 10px"> ON {$wpdb->users}.ID = {$wpdb->gp_permissions}.user_id
</span><span class="cx" style="display: block; padding: 0 10px"> WHERE action='admin'"
</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">+ );
+ } catch ( Exception $e ) {
+ $admin_email_addresses = array();
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> foreach ( $admin_email_addresses as $admin ) {
</span><span class="cx" style="display: block; padding: 0 10px"> $email_addresses[] = $admin->user_email;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -294,7 +328,9 @@
</span><span class="cx" style="display: block; padding: 0 10px"> if ( ( null === $comment ) || ( null === $comment_meta ) || ( empty( $email_addresses ) ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return 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">+ $original = self::get_original( $comment );
</ins><span class="cx" style="display: block; padding: 0 10px"> $email_addresses = self::remove_commenter_email_address( $comment, $email_addresses );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $email_addresses = self::remove_optout_discussion_email_addresses( $original->id, $email_addresses );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $headers = array(
</span><span class="cx" style="display: block; padding: 0 10px"> 'Content-Type: text/html; charset=UTF-8',
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -327,7 +363,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * @return string|null
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> public static function get_email_body( WP_Comment $comment, array $comment_meta ): string {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $project = self::get_project_to_translate( $comment );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $post = get_post( $comment->comment_post_ID );
+ $project = self::get_project_from_post( $post );
</ins><span class="cx" style="display: block; padding: 0 10px"> $original = self::get_original( $comment );
</span><span class="cx" style="display: block; padding: 0 10px"> $output = '';
</span><span class="cx" style="display: block; padding: 0 10px"> /**
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -350,34 +387,63 @@
</span><span class="cx" style="display: block; padding: 0 10px"> 'a' => array( 'href' => array() ),
</span><span class="cx" style="display: block; padding: 0 10px"> )
</span><span class="cx" style="display: block; padding: 0 10px"> ) . '<br/>';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( ! empty( self::$related_comments ) ) {
+ $output .= wp_kses(
+ /* translators: The number of different translations related with the comment. */
+ sprintf( __( 'This comment affects to <strong>%1$d different translations</strong>.', 'glotpress' ), count( self::$related_comments ) + 1 ),
+ array(
+ 'a' => array( 'href' => array() ),
+ 'strong' => array(),
+ )
+ ) . '<br/>';
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> $output .= '<br>';
</span><span class="cx" style="display: block; padding: 0 10px"> $output .= esc_html__( 'It would be nice if you have some time to review this comment and reply to it if needed.', 'glotpress' );
</span><span class="cx" style="display: block; padding: 0 10px"> $output .= '<br><br>';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $output .= '- ' . wp_kses(
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( array_key_exists( 'locale', $comment_meta ) && ( ! empty( $comment_meta['locale'][0] ) ) ) {
+ /* translators: The translation locale for the comment. */
+ $output .= '- ' . wp_kses( sprintf( __( '<strong>Locale:</strong> %s', 'glotpress' ), $comment_meta['locale'][0] ), array( 'strong' => array() ) ) . '<br/>';
+ }
+ if ( empty( self::$related_comments ) ) { // Only show original and translation strings if we don't have related comments (bulk rejection).
+ /* translators: The original string to translate. */
+ $output .= '- ' . wp_kses( sprintf( __( '<strong>Original string:</strong> %s', 'glotpress' ), $original->singular ), array( 'strong' => array() ) ) . '<br/>';
+ if ( array_key_exists( 'translation_id', $comment_meta ) && $comment_meta['translation_id'][0] ) {
+ $translation_id = $comment_meta['translation_id'][0];
+ $translation = GP::$translation->get( $translation_id );
+ // todo: add the plurals.
+ if ( ! is_null( $translation ) ) {
+ /* translators: The translation string. */
+ $output .= '- ' . wp_kses( sprintf( __( '<strong>Translation string:</strong> %s', 'glotpress' ), $translation->translation_0 ), array( 'strong' => array() ) ) . '<br/>';
+ }
+ }
+ }
+ /* translators: The comment made. */
+ $output .= '- ' . wp_kses( sprintf( __( '<strong>Comment:</strong> %s', 'glotpress' ), $comment->comment_content ), array( 'strong' => array() ) ) . '<br/>';
+ if ( empty( self::$related_comments ) ) {
+ $output .= '- ' . __( '<strong>Discussion URL:</strong>' ) . '<br/>';
+ } else {
+ $output .= '- ' . __( '<strong>Discussion URLs:</strong>' ) . '<br/>';
+ }
+ $output .= ' - ' . wp_kses(
</ins><span class="cx" style="display: block; padding: 0 10px"> /* translators: The discussion URL where the user can find the comment. */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- sprintf( __( '<strong>Discussion URL:</strong> <a href="%1$s">%1$s</a>', 'glotpress' ), $url ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ sprintf( __( '<a href="%1$s">%1$s</a>', 'glotpress' ), $url ),
</ins><span class="cx" style="display: block; padding: 0 10px"> array(
</span><span class="cx" style="display: block; padding: 0 10px"> 'strong' => array(),
</span><span class="cx" style="display: block; padding: 0 10px"> 'a' => array( 'href' => array() ),
</span><span class="cx" style="display: block; padding: 0 10px"> )
</span><span class="cx" style="display: block; padding: 0 10px"> ) . '<br/>';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( array_key_exists( 'locale', $comment_meta ) && ( ! empty( $comment_meta['locale'][0] ) ) ) {
- /* translators: The translation locale for the comment. */
- $output .= '- ' . wp_kses( sprintf( __( '<strong>Locale:</strong> %s', 'glotpress' ), $comment_meta['locale'][0] ), array( 'strong' => array() ) ) . '<br/>';
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ foreach ( self::$related_comments as $related_comment ) {
+ $original = self::get_original( $related_comment );
+ $url = GP_Route_Translation_Helpers::get_permalink( $project->path, $original->id );
+ $output .= ' - ' . wp_kses(
+ /* translators: The discussion URL where the user can find the comment. */
+ sprintf( __( '<a href="%1$s">%1$s</a>', 'glotpress' ), $url ),
+ array(
+ 'strong' => array(),
+ 'a' => array( 'href' => array() ),
+ )
+ ) . '<br/>';
</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: The original string to translate. */
- $output .= '- ' . wp_kses( sprintf( __( '<strong>Original string:</strong> %s', 'glotpress' ), $original->singular ), array( 'strong' => array() ) ) . '<br/>';
- if ( array_key_exists( 'translation_id', $comment_meta ) && $comment_meta['translation_id'][0] ) {
- $translation_id = $comment_meta['translation_id'][0];
- $translation = GP::$translation->get( $translation_id );
- // todo: add the plurals.
- if ( ! is_null( $translation ) ) {
- /* translators: The translation string. */
- $output .= '- ' . wp_kses( sprintf( __( '<strong>Translation string:</strong> %s', 'glotpress' ), $translation->translation_0 ), array( 'strong' => array() ) ) . '<br/>';
- }
- }
- /* translators: The comment made. */
- $output .= '- ' . wp_kses( sprintf( __( '<strong>Comment:</strong> %s', 'glotpress' ), $comment->comment_content ), array( 'strong' => array() ) );
</del><span class="cx" style="display: block; padding: 0 10px"> $output .= '<br><br>';
</span><span class="cx" style="display: block; padding: 0 10px"> $output .= esc_html__( 'Have a nice day!', 'glotpress' );
</span><span class="cx" style="display: block; padding: 0 10px"> $output .= '<br><br>';
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -433,17 +499,47 @@
</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">+ * Removes the opt-out emails in the current discussion.
+ *
+ * @since 0.0.2
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ * @param array $email_addresses A list of emails.
+ *
+ * @return array
+ */
+ public static function remove_optout_discussion_email_addresses( int $original_id, array $email_addresses ): array {
+ foreach ( $email_addresses as $email_address ) {
+ $user = get_user_by( 'email', $email_address );
+ $is_user_opt_out = ! empty(
+ get_users(
+ array(
+ 'meta_key' => 'gp_opt_out',
+ 'meta_value' => $original_id,
+ 'include' => array( $user->ID ),
+ )
+ )
+ );
+ if ( $is_user_opt_out ) {
+ $index = array_search( $email_address, $email_addresses, true );
+ unset( $email_addresses[ $index ] );
+ }
+ }
+
+ return array_values( $email_addresses );
+ }
+
+ /**
</ins><span class="cx" style="display: block; padding: 0 10px"> * Gets the project that the translated string belongs to.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param WP_Comment $comment The comment object.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param WP_Post $post The post object.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @return GP_Project|bool The project that the translated string belongs to.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- private static function get_project_to_translate( WP_Comment $comment ) {
- $post_id = $comment->comment_post_ID;
- $terms = wp_get_object_terms( $post_id, Helper_Translation_Discussion::LINK_TAXONOMY, array( 'number' => 1 ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private static function get_project_from_post( WP_Post $post ) {
+ $terms = wp_get_object_terms( $post->ID, Helper_Translation_Discussion::LINK_TAXONOMY, array( 'number' => 1 ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( empty( $terms ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return false;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -456,6 +552,33 @@
</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">+ * Adds a related comment (to the first one) when the validator makes a bulk rejection.
+ *
+ * @since 0.0.2
+ *
+ * @param WP_Comment $comment The related comment to add.
+ *
+ * @return void
+ */
+ public static function add_related_comment( WP_Comment $comment ) {
+ self::$related_comments[] = $comment;
+ }
+
+ /**
+ * Gets the project the original_id belongs to.
+ *
+ * @since 0.0.2
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ *
+ * @return GP_Project The project the original_id belongs to.
+ */
+ public static function get_project_from_original_id( int $original_id ): GP_Project {
+ $original = GP::$original->get( $original_id );
+ return GP::$project->get( $original->project_id );
+ }
+
+ /**
</ins><span class="cx" style="display: block; padding: 0 10px"> * Gets the original string that the translated string belongs to.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -464,7 +587,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @return GP_Thing|false The original string that the translated string belongs to.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- private static function get_original( WP_Comment $comment ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public static function get_original( WP_Comment $comment ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> $post_id = $comment->comment_post_ID;
</span><span class="cx" style="display: block; padding: 0 10px"> $terms = wp_get_object_terms( $post_id, Helper_Translation_Discussion::LINK_TAXONOMY, array( 'number' => 1 ) );
</span><span class="cx" style="display: block; padding: 0 10px"> if ( empty( $terms ) ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -473,4 +596,175 @@
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> return GP::$original->get( $terms[0]->slug );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ /**
+ * Gets the post_id for the discussion of an original_id.
+ *
+ * If the post doesn't exist, the result is 0.
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ *
+ * @return int The post_id for the discussion of an original_id.
+ */
+ public static function get_post_id( int $original_id ): int {
+ $gp_posts = get_posts(
+ array(
+ 'tax_query' => array(
+ array(
+ 'taxonomy' => Helper_Translation_Discussion::LINK_TAXONOMY,
+ 'terms' => $original_id,
+ 'field' => 'slug',
+ ),
+ ),
+ 'post_type' => Helper_Translation_Discussion::POST_TYPE,
+ 'posts_per_page' => 1,
+ 'post_status' => Helper_Translation_Discussion::POST_STATUS,
+ 'suppress_filters' => false,
+ )
+ );
+
+ return ! empty( $gp_posts ) ? $gp_posts[0]->ID : 0;
+ }
+
+ /**
+ * Returns if the given user is an GlotPress admin or not.
+ *
+ * @since 0.0.2
+ *
+ * @param WP_User $user A user object.
+ *
+ * @return bool
+ */
+ public static function is_user_an_gp_admin( WP_User $user ): bool {
+ global $wpdb;
+ try {
+ $db_email_addresses = $wpdb->get_results(
+ "
+ SELECT user_email FROM {$wpdb->users}
+ INNER JOIN {$wpdb->gp_permissions}
+ ON {$wpdb->users}.ID = {$wpdb->gp_permissions}.user_id
+ WHERE action='admin'",
+ ARRAY_N
+ );
+ foreach ( $db_email_addresses as $email_address ) {
+ $email_addresses[] = $email_address[0];
+ }
+ } catch ( Exception $e ) {
+ $email_addresses = array();
+ }
+ if ( empty( $email_addresses ) || empty( array_intersect( array( $user->user_email ), $email_addresses ) ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns if the given user is an GlotPress validator for the post or not.
+ *
+ * @since 0.0.2
+ *
+ * @param WP_User $user A user object.
+ * @param int $original_id The id of the original string used for the discussion.
+ *
+ * @return bool
+ */
+ public static function is_user_an_gp_validator( WP_User $user, int $original_id ): bool {
+ $project = self::get_project_from_original_id( $original_id );
+ $email_addresses = self::get_validators_email_addresses( $project->path );
+ if ( empty( $email_addresses ) || empty( array_intersect( array( $user->user_email ), $email_addresses ) ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Indicates whether an e-mail address is opt-out in a discussion.
+ *
+ * @since 0.0.2
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ * @param WP_User $user A user object.
+ *
+ * @return bool True if the user has opt-out, otherwise false.
+ */
+ public static function is_user_opt_out_in_discussion( int $original_id, WP_User $user ): bool {
+ return ! empty(
+ get_users(
+ array(
+ 'meta_key' => 'gp_opt_out',
+ 'meta_value' => $original_id,
+ 'include' => array( $user->ID ),
+ )
+ )
+ );
+ }
+
+ /**
+ * Gets the opt-in/oup-out message to show at the bottom of the discussions.
+ *
+ * @since 0.0.2
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ *
+ * @return string The opt-in/oup-out message to show at the bottom of the discussions.
+ */
+ public static function optin_message_for_each_discussion( int $original_id ): string {
+ $post_id = self::get_post_id( $original_id );
+ /**
+ * Filters the optin message that will be showed in each discussion.
+ *
+ * @since 0.0.2
+ *
+ * @param string $message The opt-in/oup-out message to show at the bottom of the discussions.
+ * @param int $original_id The id of the original string used for the discussion.
+ */
+ $message = apply_filters( 'gp_get_optin_message_for_each_discussion', '', $original_id );
+ if ( $message ) {
+ return $message;
+ }
+ $user = wp_get_current_user();
+ $is_user_opt_out = self::is_user_opt_out_in_discussion( $original_id, $user );
+ if ( ! $is_user_opt_out ) {
+ $comments = get_comments(
+ array(
+ 'user_id' => $user->ID,
+ 'post_id' => $post_id,
+ 'status' => 'approve',
+ 'type' => 'comment',
+ 'include_unapproved' => array( $user->ID ),
+ )
+ );
+ }
+
+ if ( $is_user_opt_out ) { // Opt-out user.
+ $output = __( 'You will not receive notifications for this discussion because you have opt-out to get notifications for it. ' );
+ $output .= ' <a href="#" class="opt-in-discussion" data-original-id="' . $original_id . '" data-opt-type="optin">' . __( 'Start receiving notifications for this discussion.' ) . '</a>';
+ return $output;
+ }
+ if ( $comments && ( ! self::is_user_an_gp_admin( $user ) ) && ( ! self::is_user_an_gp_validator( $user, $original_id ) ) ) { // Regular user with comments.
+ $output = __( 'You are going to receive notifications for the threads where you have participated. ' );
+ $output .= ' <a href="#" class="opt-out-discussion" data-original-id="' . $original_id . '" data-opt-type="optout">' . __( 'Stop receiving notifications for this discussion.' ) . '</a>';
+ return $output;
+ }
+ if ( self::is_user_an_gp_admin( $user ) && self::is_user_an_gp_validator( $user, $original_id ) ) { // Admin and validator user.
+ $output = __( 'You are going to receive notifications because you are a GlotPress administrator and a validator for this project and language. ' );
+ $output .= __( 'You will not receive notifications if another administrator or another validator participate in a thread where you do not take part. ' );
+ $output .= ' <a href="#" class="opt-out-discussion" data-original-id="' . $original_id . '" data-opt-type="optout">' . __( 'Stop receiving notifications for this discussion.' ) . '</a>';
+ return $output;
+ }
+ if ( self::is_user_an_gp_admin( $user ) ) { // Admin user.
+ $output = __( 'You are going to receive notifications because you are a GlotPress administrator. ' );
+ $output .= __( 'You will not receive notifications if another administrator participate in a thread where you do not take part. ' );
+ $output .= ' <a href="#" class="opt-out-discussion" data-original-id="' . $original_id . '" data-opt-type="optout">' . __( 'Stop receiving notifications for this discussion.' ) . '</a>';
+ return $output;
+ }
+ if ( self::is_user_an_gp_validator( $user, $original_id ) ) { // Validator user.
+ $output = __( 'You are going to receive notifications because you are a GlotPress validator for this project and language. ' );
+ $output .= __( 'You will not receive notifications if another validator participate in a thread where you do not take part. ' );
+ $output .= ' <a href="#" class="opt-out-discussion" data-original-id="' . $original_id . '" data-opt-type="optout">' . __( 'Stop receiving notifications for this discussion.' ) . '</a>';
+ return $output;
+ }
+ return __( 'You will not receive notifications for this discussion. We will send you notifications as soon as you get involved.' ); // Regular user without comments.
+
+ }
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpersincludesclassgptranslationhelpersphp"></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/gp-translation-helpers/includes/class-gp-translation-helpers.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/gp-translation-helpers/includes/class-gp-translation-helpers.php 2022-06-16 02:15:44 UTC (rev 11920)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/includes/class-gp-translation-helpers.php 2022-06-21 14:15:05 UTC (rev 11921)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -62,6 +62,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'transition_comment_status', array( 'GP_Notifications', 'on_comment_status_change' ), 10, 3 );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'gp_pre_tmpl_load', array( $this, 'register_reject_feedback_js' ), 10, 2 );
</span><span class="cx" style="display: block; padding: 0 10px"> add_action( 'wp_ajax_reject_with_feedback', array( $this, 'reject_with_feedback' ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ add_action( 'wp_ajax_optout_discussion_notifications', array( $this, 'optout_discussion_notifications' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> add_thickbox();
</span><span class="cx" style="display: block; padding: 0 10px"> gp_enqueue_style( 'thickbox' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -159,7 +160,9 @@
</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"> $translation_helpers_settings = array(
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- 'th_url' => gp_url_project( $args['project'], gp_url_join( $args['locale_slug'], $args['translation_set_slug'], '-get-translation-helpers' ) ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ 'th_url' => gp_url_project( $args['project'], gp_url_join( $args['locale_slug'], $args['translation_set_slug'], '-get-translation-helpers' ) ),
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
+ 'nonce' => wp_create_nonce( 'gp_optin_optout' ),
</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"> add_action( 'gp_head', array( $this, 'css_and_js' ), 10 );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -394,20 +397,58 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $first_translation_id = array_shift( $translation_id_array );
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> // Post comment on discussion page for the first string
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $save_feedback = $this->insert_reject_comment( $reject_comment, $first_original_id, $reject_reason, $first_translation_id, $locale_slug, $_SERVER );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $first_comment_id = $this->insert_reject_comment( $reject_comment, $first_original_id, $reject_reason, $first_translation_id, $locale_slug, $_SERVER );
</ins><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> if ( ! empty( $original_id_array ) && ! empty( $translation_id_array ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> // For other strings post link to the comment.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $reject_comment = get_comment_link( $save_feedback );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $reject_comment = get_comment_link( $first_comment_id );
</ins><span class="cx" style="display: block; padding: 0 10px"> foreach ( $original_id_array as $index => $single_original_id ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $this->insert_reject_comment( $reject_comment, $single_original_id, $reject_reason, $translation_id_array[ $index ], $locale_slug, $_SERVER );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $comment_id = $this->insert_reject_comment( $reject_comment, $single_original_id, $reject_reason, $translation_id_array[ $index ], $locale_slug, $_SERVER );
+ $comment = get_comment( $comment_id );
+ GP_Notifications::add_related_comment( $comment );
</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">+ if ( $first_comment_id ) {
+ $comment = get_comment( $first_comment_id );
+ GP_Notifications::init( $comment, null, null );
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> wp_send_json_success();
</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">+ * Adds or removes metadata to a user, related with the opt-in/opt-out status in a discussion
+ *
+ * It receives thought Ajax this data:
+ * - nonce.
+ * - originalId. The id of the original string related with the discussion.
+ * - optType:
+ * - optout. Add the metadata, to opt-out from the notifications.
+ * - optin. Removes the metadata, to opt-in from the notifications. Default status.
+ *
+ * @since 0.0.2
+ *
+ * @return void
+ */
+ public function optout_discussion_notifications() {
+ $nonce = sanitize_text_field( $_POST['data']['nonce'] );
+ if ( ! wp_verify_nonce( $nonce, 'gp_optin_optout' ) ) {
+ wp_send_json_error( esc_html__( 'Invalid nonce.' ), 403 );
+ } else {
+ $user_id = get_current_user_id();
+ $original_id = sanitize_text_field( $_POST['data']['originalId'] );
+ $opt_type = sanitize_text_field( $_POST['data']['optType'] );
+ if ( 'optout' === $opt_type ) {
+ add_user_meta( $user_id, 'gp_opt_out', $original_id );
+ } elseif ( 'optin' === $opt_type ) {
+ delete_user_meta( $user_id, 'gp_opt_out', $original_id );
+ }
+ wp_send_json_success();
+ }
+ }
+
+ /**
</ins><span class="cx" style="display: block; padding: 0 10px"> * Inserts rejection feedback as WordPress comment
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpersincludesclasswporgnotificationsphp"></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/gp-translation-helpers/includes/class-wporg-notifications.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/gp-translation-helpers/includes/class-wporg-notifications.php 2022-06-16 02:15:44 UTC (rev 11920)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/includes/class-wporg-notifications.php 2022-06-21 14:15:05 UTC (rev 11921)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -33,7 +33,15 @@
</span><span class="cx" style="display: block; padding: 0 10px"> add_filter(
</span><span class="cx" style="display: block; padding: 0 10px"> 'gp_notification_admin_email_addresses',
</span><span class="cx" style="display: block; padding: 0 10px"> function ( $email_addresses, $comment, $comment_meta ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return self::get_author_email_address( $comment, $comment_meta );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $original = GP_Notifications::get_original( $comment );
+ $email_addresses = self::get_author_email_address( $original->id );
+ $parent_comments = GP_Notifications::get_parent_comments( $comment->comment_parent );
+ $emails_from_the_thread = GP_Notifications::get_commenters_email_addresses( $parent_comments );
+ // If one author has a comment in the thread, we don't need to inform to any author, because this author will be notified in the thread.
+ if ( ( ! empty( array_intersect( $email_addresses, $emails_from_the_thread ) ) ) || in_array( $comment->comment_author_email, $email_addresses, true ) ) {
+ return array();
+ }
+ return $email_addresses;
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> 10,
</span><span class="cx" style="display: block; padding: 0 10px"> 3
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -41,7 +49,14 @@
</span><span class="cx" style="display: block; padding: 0 10px"> add_filter(
</span><span class="cx" style="display: block; padding: 0 10px"> 'gp_notification_validator_email_addresses',
</span><span class="cx" style="display: block; padding: 0 10px"> function ( $email_addresses, $comment, $comment_meta ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- return self::get_validator_email_addresses( $comment, $comment_meta );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $email_addresses = self::get_validator_email_addresses( $comment, $comment_meta );
+ $parent_comments = GP_Notifications::get_parent_comments( $comment->comment_parent );
+ $emails_from_the_thread = GP_Notifications::get_commenters_email_addresses( $parent_comments );
+ // If one validator (GTE/PTE/CLPTE) has a comment in the thread, we don't need to inform to any validator, because this validator will be notified in the thread.
+ if ( ! empty( array_intersect( $email_addresses, $emails_from_the_thread ) ) || in_array( $comment->comment_author_email, $email_addresses, true ) ) {
+ return array();
+ }
+ return $email_addresses;
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> 10,
</span><span class="cx" style="display: block; padding: 0 10px"> 3
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -71,6 +86,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"> );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ add_filter(
+ 'gp_get_optin_message_for_each_discussion',
+ function ( $message, $original_id ) {
+ return self::optin_message_for_each_discussion( $original_id );
+ },
+ 10,
+ 2
+ );
</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">@@ -88,17 +111,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * @return array The validators' emails.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> public static function get_validator_email_addresses( WP_Comment $comment, array $comment_meta ): array {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $locale = $comment_meta['locale'][0];
- $email_addresses = self::get_gte_email_addresses( $locale );
- $email_addresses = array_merge( $email_addresses, self::get_pte_email_addresses_by_project_and_locale( $comment, $locale ) );
- $email_addresses = array_merge( $email_addresses, self::get_clpte_email_addresses_by_project( $comment ) );
- $parent_comments = GP_Notifications::get_parent_comments( $comment->comment_parent );
- $emails_from_the_thread = GP_Notifications::get_commenters_email_addresses( $parent_comments );
- // Set the email addresses array as empty if one GTE/PTE/CLPTE has a comment in the thread.
- if ( ! empty( array_intersect( $email_addresses, $emails_from_the_thread ) ) || in_array( $comment->comment_author_email, $email_addresses, true ) ) {
- return array();
- }
- return $email_addresses;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $locale = $comment_meta['locale'][0];
+ $email_addresses = self::get_gte_email_addresses( $locale );
+ $original = GP_Notifications::get_original( $comment );
+ $email_addresses = array_merge( $email_addresses, self::get_pte_email_addresses_by_project_and_locale( $original->id, $locale ) );
+ return array_merge( $email_addresses, self::get_clpte_email_addresses_by_project( $original->id ) );
</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">@@ -150,13 +167,13 @@
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param WP_Comment $comment The comment object.
- * @param string $locale The locale. E.g. 'zh-tw'.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param int $original_id The id of the original string used for the discussion.
+ * @param string $locale The locale. E.g. 'zh-tw'.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @return array The project translation editors (PTE) emails.
</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 static function get_pte_email_addresses_by_project_and_locale( $comment, $locale ): array {
- return self::get_pte_clpte_email_addresses_by_project_and_locale( $comment, $locale );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public static function get_pte_email_addresses_by_project_and_locale( int $original_id, string $locale ): array {
+ return self::get_pte_clpte_email_addresses_by_project_and_locale( $original_id, $locale );
</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">@@ -164,12 +181,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param WP_Comment $comment The comment object.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param int $original_id The id of the original string used for the discussion.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @return array The cross language project translation editors (CLPTE) emails.
</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 static function get_clpte_email_addresses_by_project( $comment ): array {
- return self::get_pte_clpte_email_addresses_by_project_and_locale( $comment, 'all-locales' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public static function get_clpte_email_addresses_by_project( int $original_id ): array {
+ return self::get_pte_clpte_email_addresses_by_project_and_locale( $original_id, 'all-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><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -177,12 +194,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param WP_Comment $comment The comment object.
- * @param string $locale The locale. E.g. 'zh-tw'.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param int $original_id The id of the original string used for the discussion.
+ * @param string $locale The locale. E.g. 'zh-tw'.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @return array The PTE/CLPTE emails for the project and locale.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- private static function get_pte_clpte_email_addresses_by_project_and_locale( WP_Comment $comment, string $locale ): array {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private static function get_pte_clpte_email_addresses_by_project_and_locale( int $original_id, string $locale ): array {
</ins><span class="cx" style="display: block; padding: 0 10px"> global $wpdb;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> if ( 'all-locales' === $locale ) {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -195,8 +212,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> return array();
</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">- $project = self::get_project_to_translate( $comment );
-
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $project = self::get_project_from_original_id( $original_id );
</ins><span class="cx" style="display: block; padding: 0 10px"> // todo: remove the deleted users in the SQL query.
</span><span class="cx" style="display: block; padding: 0 10px"> $translation_editors = $wpdb->get_results(
</span><span class="cx" style="display: block; padding: 0 10px"> $wpdb->prepare(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -225,17 +241,17 @@
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * Themes: only one email.
</span><span class="cx" style="display: block; padding: 0 10px"> * Plugins: all the plugin authors.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * Other projects: the special users, available at $i18n_email.
</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 WP_Comment $comment The comment object.
- * @param array $comment_meta The meta values for the comment.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param int $original_id The id of the original string used for the discussion.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @return array The email addresses for the author of a theme or a plugin.
</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 static function get_author_email_address( WP_Comment $comment, array $comment_meta ): array {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ public static function get_author_email_address( int $original_id ): array {
</ins><span class="cx" style="display: block; padding: 0 10px"> global $wpdb;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><span class="cx" style="display: block; padding: 0 10px"> $email_addresses = array();
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $project = self::get_project_to_translate( $comment );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $project = GP_Notifications::get_project_from_original_id( $original_id );
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( 'wp-themes' === substr( $project->path, 0, 9 ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> $author = $wpdb->get_row(
</span><span class="cx" style="display: block; padding: 0 10px"> $wpdb->prepare(
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -267,12 +283,6 @@
</span><span class="cx" style="display: block; padding: 0 10px"> if ( ! ( ( 'wp-themes' === substr( $project->path, 0, 9 ) ) || ( 'wp-plugins' === substr( $project->path, 0, 10 ) ) ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> $email_addresses = self::$i18n_email;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $parent_comments = GP_Notifications::get_parent_comments( $comment->comment_parent );
- $emails_from_the_thread = GP_Notifications::get_commenters_email_addresses( $parent_comments );
- // If one author has a comment in the thread or if one validator is the commenter, we don't need to inform any other validator.
- if ( ( true !== empty( array_intersect( $email_addresses, $emails_from_the_thread ) ) ) || in_array( $comment->comment_author_email, $email_addresses, true ) ) {
- return array();
- }
</del><span class="cx" style="display: block; padding: 0 10px"> return $email_addresses;
</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">@@ -287,8 +297,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> * @return string|null The email body message.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> public static function get_email_body( WP_Comment $comment, ?array $comment_meta ): ?string {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $project = self::get_project_to_translate( $comment );
- $original = self::get_original( $comment );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ $project = self::get_project_from_post_id( $comment->comment_post_ID );
+ $original = self::get_original( $comment->comment_post_ID );
</ins><span class="cx" style="display: block; padding: 0 10px"> $output = esc_html__( 'Hi:' );
</span><span class="cx" style="display: block; padding: 0 10px"> $output .= '<br><br>';
</span><span class="cx" style="display: block; padding: 0 10px"> $output .= esc_html__( 'There is a new comment in a discussion of the WordPress translation system that may be of interest to you.' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -324,13 +334,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param WP_Comment $comment The comment object.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param int $post_id The id of the shadow post used for the discussion.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @return GP_Project The project the translated string belongs to.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @return false|GP_Project The project the translated string belongs to.
</ins><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- private static function get_project_to_translate( WP_Comment $comment ): GP_Project {
- $post_id = $comment->comment_post_ID;
- $terms = wp_get_object_terms( $post_id, Helper_Translation_Discussion::LINK_TAXONOMY, array( 'number' => 1 ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private static function get_project_from_post_id( int $post_id ) {
+ $terms = wp_get_object_terms( $post_id, Helper_Translation_Discussion::LINK_TAXONOMY, array( 'number' => 1 ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( empty( $terms ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return false;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -343,7 +352,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> // If the parent project is not a main project, get the parent project. We need to do this
</span><span class="cx" style="display: block; padding: 0 10px"> // because we have 3 levels of projects. E.g. wp-plugins->akismet->stable and the PTE are
</span><span class="cx" style="display: block; padding: 0 10px"> // assigned to the second level.
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- if ( ( ! is_null( $project->parent_project_id ) ) && ( ! in_array( $project->parent_project_id, $main_projects, true ) ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( ( ! is_null( $project->parent_project_id ) ) && ( ! in_array( $project->parent_project_id, $main_projects, false ) ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> $project = GP::$project->get( $project->parent_project_id );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> return $project;
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -350,6 +359,30 @@
</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">+ * Gets the project the original_id belongs to.
+ *
+ * @since 0.0.2
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ *
+ * @return GP_Project The project the original_id belongs to.
+ */
+ public static function get_project_from_original_id( int $original_id ): GP_Project {
+ $original = GP::$original->get( $original_id );
+ $project_id = $original->project_id;
+ $project = GP::$project->get( $project_id );
+ $main_projects = self::get_main_projects();
+
+ // If the parent project is not a main project, get the parent project. We need to do this
+ // because we have 3 levels of projects. E.g. wp-plugins->akismet->stable and the PTE are
+ // assigned to the second level.
+ if ( ( ! is_null( $project->parent_project_id ) ) && ( ! in_array( $project->parent_project_id, $main_projects, false ) ) ) {
+ $project = GP::$project->get( $project->parent_project_id );
+ }
+ return $project;
+ }
+
+ /**
</ins><span class="cx" style="display: block; padding: 0 10px"> * Gets the id of the main projects without parent projects.
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -359,9 +392,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> private static function get_main_projects():array {
</span><span class="cx" style="display: block; padding: 0 10px"> global $wpdb;
</span><span class="cx" style="display: block; padding: 0 10px">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $main_projects = $wpdb->get_col( "SELECT id FROM {$wpdb->gp_projects} WHERE parent_project_id IS NULL" );
-
- return $main_projects;
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ return $wpdb->get_col( "SELECT id FROM {$wpdb->gp_projects} WHERE parent_project_id IS NULL" );
</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">@@ -369,13 +400,12 @@
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @since 0.0.2
</span><span class="cx" style="display: block; padding: 0 10px"> *
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- * @param WP_Comment $comment The comment object.
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ * @param int $post_id The id of the shadow post used for the discussion.
</ins><span class="cx" style="display: block; padding: 0 10px"> *
</span><span class="cx" style="display: block; padding: 0 10px"> * @return GP_Thing|false The original string that the translated string belongs to.
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- private static function get_original( WP_Comment $comment ) {
- $post_id = $comment->comment_post_ID;
- $terms = wp_get_object_terms( $post_id, Helper_Translation_Discussion::LINK_TAXONOMY, array( 'number' => 1 ) );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ private static function get_original( int $post_id ) {
+ $terms = wp_get_object_terms( $post_id, Helper_Translation_Discussion::LINK_TAXONOMY, array( 'number' => 1 ) );
</ins><span class="cx" style="display: block; padding: 0 10px"> if ( empty( $terms ) ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return false;
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -394,9 +424,7 @@
</span><span class="cx" style="display: block; padding: 0 10px"> */
</span><span class="cx" style="display: block; padding: 0 10px"> private static function get_opted_in_email_addresses( array $email_addresses ): array {
</span><span class="cx" style="display: block; padding: 0 10px"> foreach ( $email_addresses as $email_address ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- $user = get_user_by( 'email', $email_address );
- $gp_default_sort = get_user_option( 'gp_default_sort', $user->ID );
- if ( 'on' != gp_array_get( $gp_default_sort, 'notifications_optin', 'off' ) ) {
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ if ( self::is_global_optout_email_address( $email_address ) ) {
</ins><span class="cx" style="display: block; padding: 0 10px"> $index = array_search( $email_address, $email_addresses, true );
</span><span class="cx" style="display: block; padding: 0 10px"> if ( false !== $index ) {
</span><span class="cx" style="display: block; padding: 0 10px"> unset( $email_addresses[ $index ] );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -405,4 +433,223 @@
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><span class="cx" style="display: block; padding: 0 10px"> return array_values( $email_addresses );
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+ /**
+ * Indicates whether a user is globally opt-out.
+ *
+ * @since 0.0.2
+ *
+ * @param string $email_address The user's email address.
+ *
+ * @return bool Whether a user wis globally opt-out.
+ */
+ private static function is_global_optout_email_address( string $email_address ): bool {
+ $user = get_user_by( 'email', $email_address );
+ $gp_default_sort = get_user_option( 'gp_default_sort', $user->ID );
+ if ( 'on' != gp_array_get( $gp_default_sort, 'notifications_optin', 'off' ) ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Indicates if the given user is a GTE at translate.wordpress.org.
+ *
+ * @since 0.0.2
+ *
+ * @todo Cache the GTE email addresses, because getting it made a lot of queries, slowing down the load time.
+ *
+ * @param WP_User $user A user object.
+ *
+ * @return bool Whether the user is GTE for any of the languages to which the comments in the post belong.
+ */
+ public static function is_user_an_wporg_gte( WP_User $user ): bool {
+ $locales = GP_Locales::locales();
+ $gte_email_addresses = array();
+ foreach ( $locales as $locale ) {
+ $gte_email_addresses = array_merge( $gte_email_addresses, self::get_gte_email_addresses( $locale->slug ) );
+ }
+ if ( empty( array_intersect( array( $user->user_email ), $gte_email_addresses ) ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Indicates if the given user is PTE for the project and for any of the languages to which the comments in the post belong.
+ *
+ * @since 0.0.2
+ *
+ * @todo Cache the PTE email addresses for each project, because getting it made a lot of queries, slowing down the load time.
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ * @param WP_User $user A user object.
+ *
+ * @return bool Whether the user is PTE for the project and for any of the languages to which the comments in the post belong.
+ */
+ public static function is_user_an_wporg_pte_for_the_project( int $original_id, WP_User $user ): bool {
+ $locales = GP_Locales::locales();
+ $pte_email_addresses = array();
+ foreach ( $locales as $locale ) {
+ $pte_email_addresses = array_merge( $pte_email_addresses, self::get_pte_email_addresses_by_project_and_locale( $original_id, $locale->slug ) );
+ }
+ if ( empty( $pte_email_addresses ) || empty( array_intersect( array( $user->user_email ), $pte_email_addresses ) ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Indicates if the given user is CLPTE for the project to which the post belong.
+ *
+ * @since 0.0.2
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ * @param WP_User $user A user object.
+ *
+ * @return bool Whether the user is a CLPTE for the project to which the post belong.
+ */
+ public static function is_user_an_wporg_clpte_for_the_project( int $original_id, WP_User $user ): bool {
+ $clpte_email_addresses = self::get_clpte_email_addresses_by_project( $original_id );
+ if ( empty( $clpte_email_addresses ) || empty( array_intersect( array( $user->user_email ), $clpte_email_addresses ) ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Indicates if the given user is the author for the project to which the post belong.
+ *
+ * Only works with plugins and themes.
+ *
+ * @since 0.0.2
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ * @param WP_User $user A user object.
+ *
+ * @return bool Whether the user is the author for the project to which the post belong.
+ */
+ public static function is_user_an_author_of_the_project( int $original_id, WP_User $user ): bool {
+ $author_email_addresses = self::get_author_email_address( $original_id );
+ if ( empty( $author_email_addresses ) || empty( array_intersect( array( $user->user_email ), $author_email_addresses ) ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Indicates if the given user is a special user for projects different that themes and plugins.
+ *
+ * @since 0.0.2
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ * @param WP_User $user A user object.
+ *
+ * @return bool Whether the user is a special user or not for projects different than themes and plugins.
+ */
+ public static function is_an_special_user_in_a_special_project( int $original_id, WP_User $user ):bool {
+ $project = self::get_project_from_original_id( $original_id );
+ if ( 'wp-themes' !== substr( $project->path, 0, 9 ) && ( 'wp-plugins' !== substr( $project->path, 0, 10 ) ) ) {
+ if ( empty( self::$i18n_email ) || empty( array_intersect( array( $user->user_email ), self::$i18n_email ) ) ) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Indicates if the given user has made a comment in the discussion.
+ *
+ * @since 0.0.2
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ * @param WP_User $user A user object.
+ *
+ * @return bool Whether the user has made a comment in the discussion.
+ */
+ private static function is_user_a_commenter_in_the_discussion( int $original_id, WP_User $user ):bool {
+ $post_id = GP_Notifications::get_post_id( $original_id );
+ $comments = get_comments(
+ array(
+ 'post_id' => $post_id,
+ 'user_id' => $user->ID,
+ 'status' => 'approve',
+ 'type' => 'comment',
+ 'include_unapproved' => array( get_current_user_id() ),
+ )
+ );
+ if ( empty( $comments ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Gets the opt-in/oup-out message to show at the bottom of the discussions at translate.wordpress.org.
+ *
+ * @param int $original_id The id of the original string used for the discussion.
+ *
+ * @return string The message to show at the bottom of the discussions at translate.wordpress.org.
+ */
+ public static function optin_message_for_each_discussion( int $original_id ): string {
+ $user = wp_get_current_user();
+
+ if ( self::is_global_optout_email_address( $user->user_email ) ) {
+ $output = __( 'You will not receive notifications because you have not yet opted-in. ' );
+ $output .= ' <a href="https://translate.wordpress.org/settings/">' . __( 'Start receiving notifications.' ) . '</a>';
+ return $output;
+ }
+ if ( GP_Notifications::is_user_opt_out_in_discussion( $original_id, $user ) ) {
+ $output = __( 'You will not receive notifications for this discussion because you have opt-out to get notifications for it. ' );
+ $output .= ' <a href="#" class="opt-in-discussion" data-original-id="' . $original_id . '" data-opt-type="optin">' . __( 'Start receiving notifications for this discussion.' ) . '</a>';
+ $output .= ' <a href="https://translate.wordpress.org/settings/">' . __( 'Stop receiving notifications.' ) . '</a>';
+ return $output;
+ }
+ if ( self::is_user_an_wporg_gte( $user ) ) {
+ $output = __( 'You are going to receive notifications for the questions in your language because you are a GTE. ' );
+ $output .= __( 'You will not receive notifications if another GTE or PTE for your language or CLPTE participates in a thread where you do not take part. ' );
+ $output .= ' <a href="#" class="opt-out-discussion" data-original-id="' . $original_id . '" data-opt-type="optout">' . __( 'Stop receiving notifications for this discussion.' ) . '</a>';
+ $output .= ' <a href="https://translate.wordpress.org/settings/">' . __( 'Stop receiving notifications.' ) . '</a>';
+ return $output;
+ }
+ if ( self::is_user_an_wporg_pte_for_the_project( $original_id, $user ) ) {
+ $output = __( 'You are going to receive notifications for the questions in your language because you are a PTE. ' );
+ $output .= __( 'You will not receive notifications if another GTE or PTE for your language or CLPTE participates in a thread where you do not take part. ' );
+ $output .= ' <a href="#" class="opt-out-discussion" data-original-id="' . $original_id . '" data-opt-type="optout">' . __( 'Stop receiving notifications for this discussion.' ) . '</a>';
+ $output .= ' <a href="https://translate.wordpress.org/settings/">' . __( 'Stop receiving notifications.' ) . '</a>';
+ return $output;
+ }
+ if ( self::is_user_an_wporg_clpte_for_the_project( $original_id, $user ) ) {
+ $output = __( 'You are going to receive notifications for the questions because you are a CLPTE. ' );
+ $output .= __( 'You will not receive notifications if another GTE or PTE for their language or CLPTE participates in a thread where you do not take part. ' );
+ $output .= ' <a href="#" class="opt-out-discussion" data-original-id="' . $original_id . '" data-opt-type="optout">' . __( 'Stop receiving notifications for this discussion.' ) . '</a>';
+ $output .= ' <a href="https://translate.wordpress.org/settings/">' . __( 'Stop receiving notifications.' ) . '</a>';
+ return $output;
+ }
+ if ( self::is_an_special_user_in_a_special_project( $original_id, $user ) ) {
+ $output = __( 'You are going to receive notifications for some questions (typos and more context) because you are a special user. ' );
+ $output .= __( 'You will not receive notifications if another special user participates in a thread where you do not take part. ' );
+ $output .= ' <a href="#" class="opt-out-discussion" data-original-id="' . $original_id . '" data-opt-type="optout">' . __( 'Stop receiving notifications for this discussion.' ) . '</a>';
+ $output .= ' <a href="https://translate.wordpress.org/settings/">' . __( 'Stop receiving notifications.' ) . '</a>';
+ return $output;
+ }
+ if ( self::is_user_an_author_of_the_project( $original_id, $user ) ) {
+ $output = __( 'You are going to receive notifications for some questions (typos and more context) because you are an author. ' );
+ $output .= __( 'You will not receive notifications if another author participates in a thread where you do not take part. ' );
+ $output .= ' <a href="#" class="opt-out-discussion" data-original-id="' . $original_id . '" data-opt-type="optout">' . __( 'Stop receiving notifications for this discussion.' ) . '</a>';
+ $output .= ' <a href="https://translate.wordpress.org/settings/">' . __( 'Stop receiving notifications.' ) . '</a>';
+ return $output;
+ }
+ if ( self::is_user_a_commenter_in_the_discussion( $original_id, $user ) ) {
+ $output = __( 'You are going to receive notifications for some threads where you have taken part. ' );
+ $output .= ' <a href="#" class="opt-out-discussion" data-original-id="' . $original_id . '" data-opt-type="optout">' . __( 'Stop receiving notifications for this discussion.' ) . '</a>';
+ $output .= ' <a href="https://translate.wordpress.org/settings/">' . __( 'Stop receiving notifications.' ) . '</a>';
+ return $output;
+ }
+ $output = __( 'You will not receive notifications for this discussion. We will send you notifications as soon as you get involved.' );
+ $output .= ' <a href="https://translate.wordpress.org/settings/">' . __( 'Stop receiving notifications.' ) . '</a>';
+ return $output;
+ }
+
</ins><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordpressorgpublic_htmlwpcontentpluginsgptranslationhelpersjstranslationhelpersjs"></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/gp-translation-helpers/js/translation-helpers.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/gp-translation-helpers/js/translation-helpers.js 2022-06-16 02:15:44 UTC (rev 11920)
+++ sites/trunk/wordpress.org/public_html/wp-content/plugins/gp-translation-helpers/js/translation-helpers.js 2022-06-21 14:15:05 UTC (rev 11921)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,4 +1,4 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-/* global $gp, window */
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/* global $gp, window, $gp_translation_helpers_settings */
</ins><span class="cx" style="display: block; padding: 0 10px"> $gp.translation_helpers = (
</span><span class="cx" style="display: block; padding: 0 10px"> function( $ ) {
</span><span class="cx" style="display: block; padding: 0 10px"> return {
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -13,7 +13,8 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $( $gp.translation_helpers.table )
</span><span class="cx" style="display: block; padding: 0 10px"> .on( 'beforeShow', '.editor', $gp.translation_helpers.hooks.initial_fetch )
</span><span class="cx" style="display: block; padding: 0 10px"> .on( 'click', '.helpers-tabs li', $gp.translation_helpers.hooks.tab_select )
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">- .on( 'click', 'a.comment-reply-link', $gp.translation_helpers.hooks.reply_comment_form );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ .on( 'click', 'a.comment-reply-link', $gp.translation_helpers.hooks.reply_comment_form )
+ .on( 'click', 'a.opt-out-discussion,a.opt-in-discussion', $gp.translation_helpers.hooks.optin_optout_discussion );
</ins><span class="cx" style="display: block; padding: 0 10px"> },
</span><span class="cx" style="display: block; padding: 0 10px"> initial_fetch: function( $element ) {
</span><span class="cx" style="display: block; padding: 0 10px"> var $helpers = $element.find( '.translation-helpers' );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -80,6 +81,27 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $comment.text( 'Reply' );
</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">+ optin_optout_discussion: function( $link ) {
+ var data = {
+ action: 'optout_discussion_notifications',
+ data: {
+ nonce: $gp_translation_helpers_settings.nonce,
+ originalId: $link.attr( 'data-original-id' ),
+ optType: $link.attr( 'data-opt-type' ),
+ },
+ };
+ $.ajax(
+ {
+ type: 'POST',
+ url: $gp_translation_helpers_settings.ajax_url,
+ data: data,
+ }
+ ).done(
+ function() {
+ $gp.translation_helpers.fetch( 'discussion' );
+ }
+ );
+ },
</ins><span class="cx" style="display: block; padding: 0 10px"> hooks: {
</span><span class="cx" style="display: block; padding: 0 10px"> initial_fetch: function() {
</span><span class="cx" style="display: block; padding: 0 10px"> $gp.translation_helpers.initial_fetch( $( this ) );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -94,6 +116,11 @@
</span><span class="cx" style="display: block; padding: 0 10px"> $gp.translation_helpers.reply_comment_form( $( this ) );
</span><span class="cx" style="display: block; padding: 0 10px"> return 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">+ optin_optout_discussion: function( event ) {
+ event.preventDefault();
+ $gp.translation_helpers.optin_optout_discussion( $( this ) );
+ return 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"> }( jQuery )
</span></span></pre>
</div>
</div>
</body>
</html>