I've been tinkering with this extension and may have solved a few problems. I do have an important usability question, however... Which messages should actually be in the Inbox? Or more specifically, which of the following is the expected behavior:
A. New messages come to you in the Inbox. Responding keeps the discussion in the Inbox. B. New messages come to you in the Inbox. Responding moves the discussion to the Sent Mail folder.
I think Option A would work best. It's what most people would expect. Also, if you send a message and it's responded too it should be in the "Inbox," too, so they can find it easier.
If you figure out a way to have it show "Sent" messages in the Sent Mail folder as well as the corresponding discussion in the "Inbox" that would be killer.
I figured "A" was best, but I've never really used any forum's PM system. That said, my users really, really want this feature.
I think I have the Inbox query worked out, but the real problem is keeping the messages in the Sent Mail folder. As I see it, there are two fields to play with: AuthUserID and LastUserID.
AuthUserID of course, is who initiated the discussion, so no problem assigning those posts to the Sent Mail folder. So what *is* the problem? Say a user responds, but didn't start the discussion... If they're the last commenter, no worries, as we can easily query that field, but what if they're somewhere in the middle? That, I have no idea how to find.
I think I have in Inbox/Sent Mail boxes working as described above. I would appreciate if someone could help test, substituting the two below functions in place of the originals in PrivateMessages/default.php:
function GetDiscussionCount($View) {
$TotalNumberOfRecords = 0;
$s = $this->Context->ObjectFactory->NewContextObject($this->Context, 'SqlBuilder');
$s->SetMainTable('Discussion', 't');
// MINOR KLUDGE: There's not apparent way w/ SqlBuilder to add 'DISTINCT' within
// the MySQL function Count(). It works just fine, but breaks the rules a bit.
$s->AddSelect('DISTINCT t.DiscussionID', '', 'Count', 'count');
$s->AddWhere('t', 'Active', '', '1', '=');
$s->AddWhere('t', 'CategoryID', '', $this->CategoryID, '=');
if ($View == 'Inbox') {
// Count discussions in the Inbox when they are addressed directly to you.
$s->AddWhere('t', 'WhisperUserID', '', $this->UserID, '=', 'and', '', 1, 1);
// Count discussions in the Inbox that *you* have sent, but only after one or
// more reply has been made. This may or may not be how you want
//this to work.
$s->AddWhere('t', 'CountComments', '', 1, '>', 'or', '', 1, 1);
$s->AddWhere('t', 'AuthUserID', '', $this->UserID, '=', 'and');
$s->EndWhereGroup();
$s->EndWhereGroup();
} else {
// Add join to access all users who have contributed to a discussion.
// Trying work this with AuthUserID or LastUserId just doesn't cut it.
$s->AddJoin('Comment', 'co', 'DiscussionID', 't', 'DiscussionID', 'join');
$s->AddWhere('co', 'AuthUserID', '', $this->UserID, '=');
}
$result = $this->Context->Database->Select($s, $this->Name, 'GetDiscussionCount', 'An error occurred while retrieving Discussion information.');
while ($rows = $this->Context->Database->GetRow($result)) {
$TotalNumberOfRecords = $rows['Count'];
}
return $TotalNumberOfRecords;
}
function GetDiscussionList($RowsPerPage, $CurrentPage, $View) {
$TotalNumberOfRecords = 0;
if ($RowsPerPage > 0) {
$CurrentPage = ForceInt($CurrentPage, 1);
if ($CurrentPage < 1) $CurrentPage == 1;
$RowsPerPage = ForceInt($RowsPerPage, 50);
$FirstRecord = ($CurrentPage * $RowsPerPage) - $RowsPerPage;
}
$s = $this->GetDiscussionBuilder();
$s->AddJoin('Comment', 'm', 'CommentID', 't', 'FirstCommentID', 'join');
$s->AddSelect('Body', 'm');
$s->AddWhere('t', 'Active', '', '1', '=');
$s->AddWhere('t', 'CategoryID', '', $this->CategoryID, '=');
if ($View == 'Inbox') {
// Display discussions in the Inbox when they are addressed directly to you.
$s->AddWhere('t', 'WhisperUserID', '', $this->UserID, '=', 'and', '', 1, 1);
// Display discussions in the Inbox that *you* have sent, but only after one or
// more reply has been made. This may or may not be how you want
//this to work.
$s->AddWhere('t', 'CountComments', '', 1, '>', 'or', '', 1, 1);
$s->AddWhere('t', 'AuthUserID', '', $this->UserID, '=', 'and');
$s->EndWhereGroup();
$s->EndWhereGroup();
} else {
// Add join to access all users who have contributed to a discussion.
// Trying work this with AuthUserID or LastUserId just doesn't cut it.
$s->AddJoin('Comment', 'co', 'DiscussionID', 't', 'DiscussionID', 'join');
$s->AddWhere('co', 'AuthUserID', '', $this->UserID, '=');
}
$s->AddOrderBy('DateLastActive', 't', 'desc');
if ($RowsPerPage > 0) $s->AddLimit($FirstRecord, $RowsPerPage);
return $this->Context->Database->Select($s, $this->Name, 'GetDiscussionList', 'An error occurred while retrieving discussions.');
}
Another issue to keep in mind: By default, this extension allows you to PM yourself. This throws in all sorts of problems with counting and sorting. For clarity, I added a bit of code to put a stop to that.
Up top at the end of the $Context->Dictionary definitions, add something along the lines of: $Context->Dictionary['ErrNoWhisperToSelf'] = 'You cannot send a Private Message to yourself. Please choose a valid recipient.';
In function PrivateMessageForm, change: if (ForceIncomingString('WhisperUsername', '') == '') {
$ResultDiscussion = false;
$this->Context->WarningCollector->Add($this->Context->GetDefinition('ErrWhisperUsernameMandatory'));
} else {
$ResultDiscussion = $dm->SaveDiscussion($this->Discussion);
} To: if (ForceIncomingString('WhisperUsername', '') == '') {
$ResultDiscussion = false;
$this->Context->WarningCollector->Add($this->Context->GetDefinition('ErrWhisperUsernameMandatory'));
} elseif(ForceIncomingString('WhisperUsername', '') == $this->Context->Session->User->Name) {
$ResultDiscussion = false;
$this->Context->WarningCollector->Add($this->Context->GetDefinition('ErrNoWhisperToSelf'));
} else {
$ResultDiscussion = $dm->SaveDiscussion($this->Discussion);
}
To make life easier on other people testing this, it might be worth packaging up everything you've done into a ZIP file for people to download? Not everyone that's happy to test is happy to hack in new code Just a friendly suggestion.
If things work out then a diff can always be made by various means to integrate the changes into the main package.
Oh Private messages will work fine with the option: "Enable Whispers" unchecked under your forum settings, but it will not send out e-mails when you get new messages unless this box is checked! Didn't know that, everything works great now with a little hacking from bshultz ;)
Ok, still tinkering with this because it's an extension I *really* need. There's a few more issues to fix and then I'll release a zip of my modified files. Of the list of bugs and feature requests posted up-thread (plus some of my own), here's the status:
Fixed: email notification a reply to a sent message does not count towards the message count in the inbox the inbox shows sent messages the date e.g. '2 hours ago' reflects the date of the first message in the exchange, not the most recent folder message counts WYSIWYG toolbar does not appear
Done: req: friendly URLs compatibility req: unread message count on the tab req: notification on the discussion list of a new PM
Unresolved: req: ability to delete messages (**No idea how to handle this)
Unknown: senders of messages are not shown in the inbox/outbox lists
hi bshultz,
Have tried your patches, they work great. Thanks.
In GetDiscussionList you have a line
$s->AddJoin('Comment', 'm', 'CommentID', 't', 'FirstCommentID', 'join');
can this be changed to
$s->AddJoin('Comment', 'm', 'CommentID', 't', 'FirstCommentID', 'left join');
I have a host with a very old Mysql 3.23.xx and this line gave me an SQL Syntax
error. With left join it seems to work fine too.
thx,
alani
Notes: 1. Feedback/Bugs I tested the modified code on a fresh Vanilla install (along with the following extensions: Wordpress Integration, BetterBBCode, BBInsertBar, Post Preview) and have Private Messages working as expected. Of course, what works for one person may well fly into a billion pieces for others. That in mind, please let me know if you find bugs or have feedback.
2. WYSIWYG Toolbar, et.al. Extensions such as the BBInsertBar and Preview Post will now work as expected with one important caveat: you must modify the desired extensions' default.php to make it aware of Private Messages.
To give an example, with the BBInsertBar, I changed the following line from: if (in_array($Context->SelfUrl, array("post.php", "comments.php")){ To: if (in_array($Context->SelfUrl, array("post.php", "comments.php"))
|| ($Context->SelfUrl == 'extension.php' && ForceIncomingString('PostBackAction', '') == 'PrivateMessages')){
3. Friendly URLs My fix for Friendly URLs might not please everyone. I would have preferred formatting URLs as such: http://some.host/extension/PrivateMessages/19/Message Due to the way URLs are formed via GetUrl(), however, I had to push everything into the query string: http://some.host/extension/?PostBackAction=PrivateMessages&DiscussionID=19&View=Message Since SEO is not a concern in this case (these are private messages after all), I hope this is acceptable to most.
Found a bug.... on the Extensions page (and maybe the rest, I dunno) the "Send Private Message" button is not below the start a discussion button, it is below the settings.
@birdmanx35: I found the "Send Private Message" button to be misplaced on the Account page. Everywhere else seemed to render as expected. Should be fixed now.
Thanks for the update, your work is much appreciated.
Earlier in the discussion, Jazzman mentioned that the tab did not display Inbox (1) or some such if there was a new message... And you said you fixed this. However I have not received any new messages so I can't know if you added this...
I have two other feature requests, both of which I think should be easy...
1) If you fixed the Inbox tab display, could you mimic this on the panel, so that if there were new messages it would say so in the Private Messages block?
2) How does one integrate TinyMCE and Private Messages? I didn't understand your BBInsertbar comments so I can't possibly convert to TinyMCE.
Again, many thanks!
Refound the bug... except my Vanilla install is now very slow, don't know if the new PM is related or not.
The bug is showing up on all settings pages for me, but is no longer on the Account Page. BTW, the server's back, no worries.
Comments
A. New messages come to you in the Inbox. Responding keeps the discussion in the Inbox.
B. New messages come to you in the Inbox. Responding moves the discussion to the Sent Mail folder.
If you figure out a way to have it show "Sent" messages in the Sent Mail folder as well as the corresponding discussion in the "Inbox" that would be killer.
I think I have the Inbox query worked out, but the real problem is keeping the messages in the Sent Mail folder. As I see it, there are two fields to play with: AuthUserID and LastUserID.
AuthUserID of course, is who initiated the discussion, so no problem assigning those posts to the Sent Mail folder. So what *is* the problem? Say a user responds, but didn't start the discussion... If they're the last commenter, no worries, as we can easily query that field, but what if they're somewhere in the middle? That, I have no idea how to find.
function GetDiscussionCount($View) { $TotalNumberOfRecords = 0; $s = $this->Context->ObjectFactory->NewContextObject($this->Context, 'SqlBuilder'); $s->SetMainTable('Discussion', 't'); // MINOR KLUDGE: There's not apparent way w/ SqlBuilder to add 'DISTINCT' within // the MySQL function Count(). It works just fine, but breaks the rules a bit. $s->AddSelect('DISTINCT t.DiscussionID', '', 'Count', 'count'); $s->AddWhere('t', 'Active', '', '1', '='); $s->AddWhere('t', 'CategoryID', '', $this->CategoryID, '='); if ($View == 'Inbox') { // Count discussions in the Inbox when they are addressed directly to you. $s->AddWhere('t', 'WhisperUserID', '', $this->UserID, '=', 'and', '', 1, 1); // Count discussions in the Inbox that *you* have sent, but only after one or // more reply has been made. This may or may not be how you want //this to work. $s->AddWhere('t', 'CountComments', '', 1, '>', 'or', '', 1, 1); $s->AddWhere('t', 'AuthUserID', '', $this->UserID, '=', 'and'); $s->EndWhereGroup(); $s->EndWhereGroup(); } else { // Add join to access all users who have contributed to a discussion. // Trying work this with AuthUserID or LastUserId just doesn't cut it. $s->AddJoin('Comment', 'co', 'DiscussionID', 't', 'DiscussionID', 'join'); $s->AddWhere('co', 'AuthUserID', '', $this->UserID, '='); } $result = $this->Context->Database->Select($s, $this->Name, 'GetDiscussionCount', 'An error occurred while retrieving Discussion information.'); while ($rows = $this->Context->Database->GetRow($result)) { $TotalNumberOfRecords = $rows['Count']; } return $TotalNumberOfRecords; } function GetDiscussionList($RowsPerPage, $CurrentPage, $View) { $TotalNumberOfRecords = 0; if ($RowsPerPage > 0) { $CurrentPage = ForceInt($CurrentPage, 1); if ($CurrentPage < 1) $CurrentPage == 1; $RowsPerPage = ForceInt($RowsPerPage, 50); $FirstRecord = ($CurrentPage * $RowsPerPage) - $RowsPerPage; } $s = $this->GetDiscussionBuilder(); $s->AddJoin('Comment', 'm', 'CommentID', 't', 'FirstCommentID', 'join'); $s->AddSelect('Body', 'm'); $s->AddWhere('t', 'Active', '', '1', '='); $s->AddWhere('t', 'CategoryID', '', $this->CategoryID, '='); if ($View == 'Inbox') { // Display discussions in the Inbox when they are addressed directly to you. $s->AddWhere('t', 'WhisperUserID', '', $this->UserID, '=', 'and', '', 1, 1); // Display discussions in the Inbox that *you* have sent, but only after one or // more reply has been made. This may or may not be how you want //this to work. $s->AddWhere('t', 'CountComments', '', 1, '>', 'or', '', 1, 1); $s->AddWhere('t', 'AuthUserID', '', $this->UserID, '=', 'and'); $s->EndWhereGroup(); $s->EndWhereGroup(); } else { // Add join to access all users who have contributed to a discussion. // Trying work this with AuthUserID or LastUserId just doesn't cut it. $s->AddJoin('Comment', 'co', 'DiscussionID', 't', 'DiscussionID', 'join'); $s->AddWhere('co', 'AuthUserID', '', $this->UserID, '='); } $s->AddOrderBy('DateLastActive', 't', 'desc'); if ($RowsPerPage > 0) $s->AddLimit($FirstRecord, $RowsPerPage); return $this->Context->Database->Select($s, $this->Name, 'GetDiscussionList', 'An error occurred while retrieving discussions.'); }
Another issue to keep in mind:
By default, this extension allows you to PM yourself. This throws in all sorts of problems with counting and sorting. For clarity, I added a bit of code to put a stop to that.
Up top at the end of the $Context->Dictionary definitions, add something along the lines of:
$Context->Dictionary['ErrNoWhisperToSelf'] = 'You cannot send a Private Message to yourself. Please choose a valid recipient.';
In function PrivateMessageForm, change:
if (ForceIncomingString('WhisperUsername', '') == '') { $ResultDiscussion = false; $this->Context->WarningCollector->Add($this->Context->GetDefinition('ErrWhisperUsernameMandatory')); } else { $ResultDiscussion = $dm->SaveDiscussion($this->Discussion); }
To:
if (ForceIncomingString('WhisperUsername', '') == '') { $ResultDiscussion = false; $this->Context->WarningCollector->Add($this->Context->GetDefinition('ErrWhisperUsernameMandatory')); } elseif(ForceIncomingString('WhisperUsername', '') == $this->Context->Session->User->Name) { $ResultDiscussion = false; $this->Context->WarningCollector->Add($this->Context->GetDefinition('ErrNoWhisperToSelf')); } else { $ResultDiscussion = $dm->SaveDiscussion($this->Discussion); }
If things work out then a diff can always be made by various means to integrate the changes into the main package.
Fixed:
email notification
a reply to a sent message does not count towards the message count in the inbox
the inbox shows sent messages
the date e.g. '2 hours ago' reflects the date of the first message in the exchange, not the most recent
folder message counts
WYSIWYG toolbar does not appear
Done:
req: friendly URLs compatibility
req: unread message count on the tab
req: notification on the discussion list of a new PM
Unresolved:
req: ability to delete messages (**No idea how to handle this)
Unknown:
senders of messages are not shown in the inbox/outbox lists
My modified version of the Private Messages extension can be found here:
http://www.sendspace.com/file/q0vl9j
http://www.sendspace.com/file/qpyzi6 (updated)
Notes:
1. Feedback/Bugs
I tested the modified code on a fresh Vanilla install (along with the following extensions: Wordpress Integration, BetterBBCode, BBInsertBar, Post Preview) and have Private Messages working as expected. Of course, what works for one person may well fly into a billion pieces for others. That in mind, please let me know if you find bugs or have feedback.
2. WYSIWYG Toolbar, et.al.
Extensions such as the BBInsertBar and Preview Post will now work as expected with one important caveat: you must modify the desired extensions' default.php to make it aware of Private Messages.
To give an example, with the BBInsertBar, I changed the following line from:
if (in_array($Context->SelfUrl, array("post.php", "comments.php")){
To:
if (in_array($Context->SelfUrl, array("post.php", "comments.php")) || ($Context->SelfUrl == 'extension.php' && ForceIncomingString('PostBackAction', '') == 'PrivateMessages')){
3. Friendly URLs
My fix for Friendly URLs might not please everyone. I would have preferred formatting URLs as such:
http://some.host/extension/PrivateMessages/19/Message
Due to the way URLs are formed via GetUrl(), however, I had to push everything into the query string:
http://some.host/extension/?PostBackAction=PrivateMessages&DiscussionID=19&View=Message
Since SEO is not a concern in this case (these are private messages after all), I hope this is acceptable to most.
Updated zip:
http://www.sendspace.com/file/qpyzi6
Also: what is the Inbox tab display issue?