<!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>[BuddyPress][9335] trunk: Messages: Support meta queries in `bp_has_message_threads()` stack.</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 { 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://buddypress.trac.wordpress.org/changeset/9335">9335</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://buddypress.trac.wordpress.org/changeset/9335","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>r-a-y</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2015-01-09 19:54:02 +0000 (Fri, 09 Jan 2015)</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'>Messages: Support meta queries in `bp_has_message_threads()` stack.

Much like <a href="http://buddypress.trac.wordpress.org/changeset/6948">r6948</a>, <a href="http://buddypress.trac.wordpress.org/changeset/6950">r6950</a>, this commit adds support for meta queries, but for
the messages component.

This will allow plugin develoeprs to customize the fetching of message
threads based on data stored in the messagemeta table, using a
powerful syntax familiar from `WP_Query`.

Commit also adds unit tests for the `'meta_query'` parameter in
`bp_has_message_threads()`.

Fixes <a href="http://buddypress.trac.wordpress.org/ticket/6063">#6063</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcbpmessagesbpmessagesclassesphp">trunk/src/bp-messages/bp-messages-classes.php</a></li>
<li><a href="#trunksrcbpmessagesbpmessagestemplatephp">trunk/src/bp-messages/bp-messages-template.php</a></li>
<li><a href="#trunktestsphpunittestcasesmessagestemplatephp">trunk/tests/phpunit/testcases/messages/template.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcbpmessagesbpmessagesclassesphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/bp-messages/bp-messages-classes.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-messages/bp-messages-classes.php     2015-01-09 19:41:33 UTC (rev 9334)
+++ trunk/src/bp-messages/bp-messages-classes.php       2015-01-09 19:54:02 UTC (rev 9335)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -359,11 +359,16 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'type'         => 'all',
</span><span class="cx" style="display: block; padding: 0 10px">                        'limit'        => null,
</span><span class="cx" style="display: block; padding: 0 10px">                        'page'         => null,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        'search_terms' => ''
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 'search_terms' => '',
+                       'meta_query'   => array()
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px">                $r = wp_parse_args( $args, $defaults );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                $pag_sql = $type_sql = $search_sql = $user_id_sql = $sender_sql = '';
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                $meta_query_sql = array(
+                       'join'  => '',
+                       'where' => ''
+               );
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                if ( $r['limit'] && $r['page'] ) {
</span><span class="cx" style="display: block; padding: 0 10px">                        $pag_sql = $wpdb->prepare( " LIMIT %d, %d", intval( ( $r['page'] - 1 ) * $r['limit'] ), intval( $r['limit'] ) );
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -390,11 +395,20 @@
</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">+                // Process meta query into SQL
+               $meta_query = self::get_meta_query_sql( $r['meta_query'] );
+               if ( ! empty( $meta_query['join'] ) ) {
+                       $meta_query_sql['join'] = $meta_query['join'];
+               }
+               if ( ! empty( $meta_query['where'] ) ) {
+                       $meta_query_sql['where'] = $meta_query['where'];
+               }
+
</ins><span class="cx" style="display: block; padding: 0 10px">                 // set up SQL array
</span><span class="cx" style="display: block; padding: 0 10px">                $sql = array();
</span><span class="cx" style="display: block; padding: 0 10px">                $sql['select'] = 'SELECT m.thread_id, MAX(m.date_sent) AS date_sent';
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                $sql['from']   = "FROM {$bp->messages->table_name_recipients} r INNER JOIN {$bp->messages->table_name_messages} m ON m.thread_id = r.thread_id";
-               $sql['where']  = "WHERE r.is_deleted = 0 {$user_id_sql} {$sender_sql} {$type_sql} {$search_sql}";
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+         $sql['from']   = "FROM {$bp->messages->table_name_recipients} r INNER JOIN {$bp->messages->table_name_messages} m ON m.thread_id = r.thread_id {$meta_query_sql['join']}";
+               $sql['where']  = "WHERE r.is_deleted = 0 {$user_id_sql} {$sender_sql} {$type_sql} {$search_sql} {$meta_query_sql['where']}";
</ins><span class="cx" style="display: block; padding: 0 10px">                 $sql['misc']   = "GROUP BY m.thread_id ORDER BY date_sent DESC {$pag_sql}";
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">                // get thread IDs
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -436,6 +450,39 @@
</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">+         * Get the SQL for the 'meta_query' param in BP_Messages_Thread::get_current_threads_for_user().
+        *
+        * We use WP_Meta_Query to do the heavy lifting of parsing the meta_query array
+        * and creating the necessary SQL clauses.
+        *
+        * @since BuddyPress (2.2.0)
+        *
+        * @param array $meta_query An array of meta_query filters. See the
+        *   documentation for WP_Meta_Query for details.
+        * @return array $sql_array 'join' and 'where' clauses.
+        */
+       public static function get_meta_query_sql( $meta_query = array() ) {
+               global $wpdb;
+
+               $sql_array = array(
+                       'join'  => '',
+                       'where' => '',
+               );
+
+               if ( ! empty( $meta_query ) ) {
+                       $meta_query = new WP_Meta_Query( $meta_query );
+
+                       // WP_Meta_Query expects the table name at
+                       // $wpdb->messagemeta
+                       $wpdb->messagemeta = buddypress()->messages->table_name_meta;
+
+                       return $meta_query->get_sql( 'message', 'm', 'id' );
+               }
+
+               return $sql_array;
+       }
+
+       /**
</ins><span class="cx" style="display: block; padding: 0 10px">          * Mark a thread as read.
</span><span class="cx" style="display: block; padding: 0 10px">         *
</span><span class="cx" style="display: block; padding: 0 10px">         * @since BuddyPress (1.0.0)
</span></span></pre></div>
<a id="trunksrcbpmessagesbpmessagestemplatephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/src/bp-messages/bp-messages-template.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/src/bp-messages/bp-messages-template.php    2015-01-09 19:41:33 UTC (rev 9334)
+++ trunk/src/bp-messages/bp-messages-template.php      2015-01-09 19:54:02 UTC (rev 9335)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -381,6 +381,8 @@
</span><span class="cx" style="display: block; padding: 0 10px">  *           the value of $_REQUEST['s'].
</span><span class="cx" style="display: block; padding: 0 10px">  *     @type string $page_arg URL argument used for the pagination param.
</span><span class="cx" style="display: block; padding: 0 10px">  *           Default: 'mpage'.
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+ *     @type array $meta_query Meta query arguments. Only applicable if $box is
+ *           not 'notices'. See WP_Meta_Query more details.
</ins><span class="cx" style="display: block; padding: 0 10px">  * }
</span><span class="cx" style="display: block; padding: 0 10px">  * @return bool True if there are threads to display, otherwise false.
</span><span class="cx" style="display: block; padding: 0 10px">  */
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -405,6 +407,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                'type'         => 'all',
</span><span class="cx" style="display: block; padding: 0 10px">                'search_terms' => isset( $_REQUEST['s'] ) ? stripslashes( $_REQUEST['s'] ) : '',
</span><span class="cx" style="display: block; padding: 0 10px">                'page_arg'     => 'mpage', // See https://buddypress.trac.wordpress.org/ticket/3679
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                'meta_query'   => array()
</ins><span class="cx" style="display: block; padding: 0 10px">         ), 'has_message_threads' );
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><span class="cx" style="display: block; padding: 0 10px">        // If trying to access notices without capabilities, redirect to root domain
</span></span></pre></div>
<a id="trunktestsphpunittestcasesmessagestemplatephp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: trunk/tests/phpunit/testcases/messages/template.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- trunk/tests/phpunit/testcases/messages/template.php       2015-01-09 19:41:33 UTC (rev 9334)
+++ trunk/tests/phpunit/testcases/messages/template.php 2015-01-09 19:54:02 UTC (rev 9335)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -68,4 +68,193 @@
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertEquals( 1, $messages_template->thread_count );
</span><span class="cx" style="display: block; padding: 0 10px">                $this->assertSame( array( $t1 ), wp_list_pluck( $messages_template->threads, 'thread_id' ) );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+       /**
+        * @group bp_has_message_threads
+        * @group meta_query
+        */
+       public function test_thread_has_messages_meta_query() {
+               $u1 = $this->factory->user->create();
+               $u2 = $this->factory->user->create();
+
+               // create some threads
+               $t1 = $this->factory->message->create( array(
+                       'sender_id'  => $u1,
+                       'recipients' => array( $u2 ),
+                       'subject'    => 'This is a knive',
+               ) );
+               $t2 = $this->factory->message->create( array(
+                       'sender_id'  => $u1,
+                       'recipients' => array( $u2 ),
+                       'subject'    => 'Oy',
+               ) );
+
+               // misc threads
+               $this->factory->message->create_many( 3, array(
+                       'sender_id'  => $u1,
+                       'recipients' => array( $u2 ),
+               ) );
+
+               // create some replies for thread 1
+               $this->factory->message->create( array(
+                       'thread_id'  => $t1,
+                       'sender_id'  => $u2,
+                       'recipients' => array( $u1 ),
+                       'content'    => "That's a spoon",
+               ) );
+               $this->factory->message->create( array(
+                       'thread_id'  => $t1,
+                       'sender_id'  => $u1,
+                       'recipients' => array( $u2 ),
+                       'content'    => "I see you've played knivey-spooney before.",
+               ) );
+               $this->factory->message->create( array(
+                       'thread_id'  => $t1,
+                       'sender_id'  => $u2,
+                       'recipients' => array( $u1 ),
+                       'content'    => '*crickets*',
+               ) );
+
+               // create some replies for thread 2
+               $this->factory->message->create( array(
+                       'thread_id'  => $t2,
+                       'sender_id'  => $u2,
+                       'recipients' => array( $u1 ),
+                       'content'    => "Oy yourself.",
+               ) );
+
+               // grab the message ids as individual variables for thread 1
+               $thread = new BP_Messages_Thread( $t1 );
+               $mids = wp_list_pluck( $thread->messages, 'id' );
+               list( $m1, $m2, $m3, $m4 ) = $mids;
+
+               // grab the message ids as individual variables for thread 2
+               $thread = new BP_Messages_Thread( $t2 );
+               $mids = wp_list_pluck( $thread->messages, 'id' );
+               list( $m5, $m6 ) = $mids;
+
+               // add meta for some of the messages
+               bp_messages_update_meta( $m1, 'utensil',  'knive' );
+               bp_messages_update_meta( $m1, 'is_knive', 'yes' );
+               bp_messages_update_meta( $m1, "starred_by_user_{$u2}", true );
+
+               bp_messages_update_meta( $m2, 'utensil',  'spoon' );
+               bp_messages_update_meta( $m2, 'is_knive', 'no' );
+               bp_messages_update_meta( $m2, 'is_spoon', 'yes' );
+
+               bp_messages_update_meta( $m3, "starred_by_user_{$u2}", true );
+
+               bp_messages_update_meta( $m5, "starred_by_user_{$u2}", true );
+
+               // now, do the message thread loop query
+               global $messages_template;
+               bp_has_message_threads( array(
+                       'user_id' => $u2,
+                       'meta_query' => array(
+                               array(
+                                       'key' => "starred_by_user_{$u2}"
+                               ),
+                       )
+               ) );
+
+               $this->assertEquals( 2, $messages_template->thread_count );
+               $this->assertEqualSets( array( $t1, $t2 ), wp_list_pluck( $messages_template->threads, 'thread_id' ) );
+       }
+
+       /**
+        * @group bp_has_message_threads
+        * @group meta_query
+        */
+       public function test_thread_has_messages_meta_query_multiple_clauses_relation_and() {
+               $u1 = $this->factory->user->create();
+               $u2 = $this->factory->user->create();
+
+               // create some threads
+               $t1 = $this->factory->message->create( array(
+                       'sender_id'  => $u1,
+                       'recipients' => array( $u2 ),
+                       'subject'    => 'This is a knive',
+               ) );
+               $t2 = $this->factory->message->create( array(
+                       'sender_id'  => $u1,
+                       'recipients' => array( $u2 ),
+                       'subject'    => 'Oy',
+               ) );
+
+               // misc threads
+               $this->factory->message->create_many( 3, array(
+                       'sender_id'  => $u1,
+                       'recipients' => array( $u2 ),
+               ) );
+
+               // create some replies for thread 1
+               $this->factory->message->create( array(
+                       'thread_id'  => $t1,
+                       'sender_id'  => $u2,
+                       'recipients' => array( $u1 ),
+                       'content'    => "That's a spoon",
+               ) );
+               $this->factory->message->create( array(
+                       'thread_id'  => $t1,
+                       'sender_id'  => $u1,
+                       'recipients' => array( $u2 ),
+                       'content'    => "I see you've played knivey-spooney before.",
+               ) );
+               $this->factory->message->create( array(
+                       'thread_id'  => $t1,
+                       'sender_id'  => $u2,
+                       'recipients' => array( $u1 ),
+                       'content'    => '*crickets*',
+               ) );
+
+               // create some replies for thread 2
+               $this->factory->message->create( array(
+                       'thread_id'  => $t2,
+                       'sender_id'  => $u2,
+                       'recipients' => array( $u1 ),
+                       'content'    => "Oy yourself.",
+               ) );
+
+               // grab the message ids as individual variables for thread 1
+               $thread = new BP_Messages_Thread( $t1 );
+               $mids = wp_list_pluck( $thread->messages, 'id' );
+               list( $m1, $m2, $m3, $m4 ) = $mids;
+
+               // grab the message ids as individual variables for thread 2
+               $thread = new BP_Messages_Thread( $t2 );
+               $mids = wp_list_pluck( $thread->messages, 'id' );
+               list( $m5, $m6 ) = $mids;
+
+               // add meta for some of the messages
+               bp_messages_update_meta( $m1, 'utensil',  'knive' );
+               bp_messages_update_meta( $m1, 'is_knive', 'yes' );
+               bp_messages_update_meta( $m1, "starred_by_user_{$u2}", true );
+
+               bp_messages_update_meta( $m2, 'utensil',  'spoon' );
+               bp_messages_update_meta( $m2, 'is_knive', 'no' );
+               bp_messages_update_meta( $m2, 'is_spoon', 'yes' );
+
+               bp_messages_update_meta( $m3, "starred_by_user_{$u2}", true );
+
+               bp_messages_update_meta( $m5, "starred_by_user_{$u2}", true );
+
+               // now, do the message thread loop query
+               global $messages_template;
+               bp_has_message_threads( array(
+                       'user_id' => $u2,
+                       'meta_query' => array(
+                               'relation' => 'AND',
+                               array(
+                                       'key' => "starred_by_user_{$u2}"
+                               ),
+                               array(
+                                       'key' => 'utensil',
+                                       'value' => 'knive',
+                               ),
+                       )
+               ) );
+
+               $this->assertEquals( 1, $messages_template->thread_count );
+               $this->assertEqualSets( array( $t1 ), wp_list_pluck( $messages_template->threads, 'thread_id' ) );
+       }
</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>
</div>

</body>
</html>