/    Sign up×
Community /Pin to ProfileBookmark

Help with adding multiple recipients to our private message system

Hi guys.

I’m hoping you can help me out please.

We have a private messaging system which allows user->user communication, and also for a “Group” message to be sent to a whole group and all its members.

There’s one annoying thing though, at present, only one recipient can be selected, or one group.

We want to update the attached code to allow for up to 5 recipients (in the “send” case) and up to 3 groups (in the “group case”) to be sent.

I’ve tried doing this myself, but I’m getting really confused on how to do it.

As you will see with the code, the user->user communication (“send” case) checks that the sender hasn’t sent too many messages in a certain time, and that the recipent’s inbox isn’t full.

I was wondering if anyone could help me updating the below code to work with sending to more than one recipient and group but to still apply the same checks? One other thing I cannot figure out is how to check that the same recipient on the “send” case isn’t listed twice – so they don’t receive the same message twice. This is already done with the “group” case with SELECT DISTINCT

Also, for the Group message, it states in the message which group it was sent to – I was hoping it could state all the groups.

I’m hoping this makes sense I’ve tried to scale it down so just the basics are there – hope that helps.

Thanks in advance.

to post a comment
PHP

28 Comments(s)

Copy linkTweet thisAlerts:
@scragarJan 07.2009 — I'll look over your code in a little while, clean a few things up that are annoying me( "SELECT COUNT(1) FROM ..." is quicker than "SELECT * FROM ....", and you don't need to use MySQL_num_rows as well, which is going to be rather slow for 400+ rows), but the basic idea would be to take the name/group as a comma seperated list, use the [url=http://php.net/explode]explode()[/url] function to break it into an array, then use a foreach on the array to check if each user has the space, and store said PM in the inbox(or however you want to do that).
Copy linkTweet thisAlerts:
@DanUKauthorJan 07.2009 — Thank you so much ?

I'll look into those comments too
Copy linkTweet thisAlerts:
@DanUKauthorJan 09.2009 — Hi scragar,

I updated most of my code with that COUNT() instead, indeed it seems much more responsive

Any luck with the code at all? Thank you!
Copy linkTweet thisAlerts:
@scragarJan 09.2009 — Oh, sorry, I got distracted, I've got a copy, but it's late now(going to bed early), I'll drop myself a reminder email ready for tomorrow, make sure it's the first thing I look at.

One thing I'm curious about though, what is the deal with the usernames, don't you check they are valid before you check if they can receive a PM?

I ask only because if you are permitting multiple PMs at once it makes sense to at least check if the user exists, before you go searching to see if they have any messages.
Copy linkTweet thisAlerts:
@DanUKauthorJan 09.2009 — Hi scragar, I'm really thankful for your help ?

All of that is in the code I missed out to make the .txt smaller, we verify it yes, and also the "to" field is a select menu of all usernames

Regards
Copy linkTweet thisAlerts:
@scragarJan 10.2009 — It needs some work, it's not a perfect rewrite, but I'm sure it's close to being what you want, maybe throw in a check, something like:
[code=php]
if(count($toAr) > 5){
$write = 0;
$msg .= 'Too many recipients';
}[/code]

Oh, and you need to make the list available to select multiple names, so add [] after the field name.

I'm more worried about the error checking you didn't show, I wanted to make sure i didn't do anything too hard to understand or restrictive, in case you would have difficulty later.
Copy linkTweet thisAlerts:
@DanUKauthorJan 11.2009 — Thanks so much for that scragar, it gives me something to work with ?

Really appreciate it
Copy linkTweet thisAlerts:
@DanUKauthorJan 11.2009 — Hi scragar, hope you're okay

I tried applying this to the current code, and I don't get any errors (great!) but I have a few issues:

When it sends a PM to multiple users, the "recipient" ends up like Nick1,Nick2,Nick3, and that's how it appears in the 'to' field in the database, rather than sending individual inserts for each user?

Also, is there a way to show a message showing who it successfully delivered to if there's more than one recipient?

Lastly, would I use the same kind of setup for the "group" part I mentioned originally?

Sorry to be a pain. Thanks
Copy linkTweet thisAlerts:
@scragarJan 11.2009 — The system should store a Nick1,Nick2,Nick3 style list if you want to save the message, but either way it should save one copy per person. I think this makes things much easier to manage, if you don't like it you can edit it, and change that.

I have no idea how you would get started with a group, the only thing I can think of would be to have each group in a separate table, letting you make a list of users in a group, then you can just do [url=http://php.net/array_unique]array_unique()[/url] to get rid of any duplicates, incase someone appears in multiple groups.

After that I assume the current code could be modified, honestly though I have no idea how you have approached the idea of groups, so I left it off, assuming you would have been able to add it yourself.

pm me your more complete code, and a brief idea of the table structure, and I'll see what I can do.
Copy linkTweet thisAlerts:
@DanUKauthorJan 12.2009 — Hi scragar, thanks again for your continued support

The group pm is pretty much all in that original code I posted under the case "group";

The database structure is as follows:


-- </H2>
<H2>-- Table structure for table <C><span><code>privmsg</code></span></C>
-- </H2>

CREATE TABLE <C><span><code>privmsg</code></span></C> (<br/>
<C><span><code>id</code></span></C> int(11) NOT NULL auto_increment,<br/>
<C><span><code>to_id</code></span></C> varchar(40) NOT NULL,<br/>
<C><span><code>from_id</code></span></C> varchar(40) NOT NULL,<br/>
<C><span><code>time_sent</code></span></C> datetime NOT NULL,<br/>
<C><span><code>subject</code></span></C> varchar(50) NOT NULL default '',<br/>
<C><span><code>message</code></span></C> text NOT NULL,<br/>
<C><span><code>opened</code></span></C> char(1) NOT NULL default 'n',<br/>
<C><span><code>time_opened</code></span></C> datetime default NULL,<br/>
<C><span><code>deleted_from</code></span></C> int(11) NOT NULL default '0',<br/>
<C><span><code>deleted_to</code></span></C> int(11) NOT NULL default '0',<br/>
PRIMARY KEY (<C><span><code>id</code></span></C>)<br/>
) ENGINE=InnoDB AUTO_INCREMENT=71131 DEFAULT CHARSET=latin1 AUTO_INCREMENT=71131 ;<br/>


When I used the code you posted, as I said no errors showed up, but it seems like it just did one INSERT and the to_id field had Nick1,Nick2,Nick3 instead of separate INSERTS.

Thanks ?

Copy linkTweet thisAlerts:
@scragarJan 12.2009 — Oh, you are right, I redied the sql, never ran it, I wanted to add one more check first, but I figured it would be OK, so I removed the extra checks.
[code=php] foreach($toAr as $to){
$sql = "INSERT INTO privmsg (to_id, from_id, time_sent, subject, message, deleted_from, deleted_to) VALUES ('".mysql_real_escape_string($to)."', '".mysql_real_escape_string($from)."', '".mysql_real_escape_string($dateadded)."', '".mysql_real_escape_string($sub)."', '".mysql_real_escape_string($message)."', 1, 0)";

// add these two lines:
if( ! mysql_query2($sql))
echo " <ul>n <li><span>Database error; could not send message to {$to} (".mysql_error().")</span></li>n </ul>nn";
// and that should work.
}[/code]

OK, I guessed at that table structure(although the deleted bits could easily be represented with int(1) to save memory, since it's only a boolean value).

I meant for the groups.
Copy linkTweet thisAlerts:
@DanUKauthorJan 12.2009 — Excellent, that's working great now ?

Really, the only thing that would be great to have some help with for the "group" case in that original pm.txt, is how I could make it send to more than one group, and only select DISTINCT accounts if the person is in more than one group. Because the groups are done in SQL (all included in that original .txt) I can't really use the same method we've done with the individual messaging

Thanks again, you're a star
Copy linkTweet thisAlerts:
@scragarJan 12.2009 — Oh, just noticed the group code, I spotted the HTML and stuff and assumed that it would all be HTML after there, I feel so foolish right now ?

something like this would work:
[code=php]
$groups = is_array($_POST['group'])?$_POST['group']:array($_POST['group']);

//...
if(in_array('ALL', $groups))// send to all members, like you are doing
// ...
}else{// now for the hard part, getting the users who's group in an array is set in an integer, horrible.
foreach($groups as $k=>$group)
$groups[$k] = mysql_real_escape_string($group);
$users_to_notify=mysql_query2("SELECT DISTINCT account FROM user WHERE deleted=0 AND ( " . implode( '=1 OR ', $groups) . "=1)");
///...
[/code]

I've not tested this, but I think it should work.
Copy linkTweet thisAlerts:
@DanUKauthorJan 12.2009 — Hi scragar, think we're almost there!

One problem with the group sending now (gah!)

Here's the code I've got (well, the changes). The problem is that it seems to be sending it way too many times, i.e. if there should be 4 recipients and there's 3 groups, 12 messages are sent. Hoping this makes sense

Thanks!

[code=php]
// Note array_unique() here for unique accounts; no duplicates
$groups = !is_array($_POST['group']) ? $_POST['group'] : array_unique($_POST['group']);
foreach($groups as $i=>$group) {
if(empty($group)) {
unset($groups[$i]);
} else {
$groups[$i] = htmlentities($group);
}
}
$from = $_SESSION['account'];
// blah blah for the rest; the same

// Send to everyone
if (in_array('All', $groups)) {
// Find ALL accounts from * user
$users_to_notify=mysql_query2("SELECT * FROM user WHERE deleted=0");
$recipient_total = mysql_num_rows($users_to_notify);
// Notify all found users
while ($user_to_notify=mysql_fetch_array($users_to_notify))
$groupnotify = mysql_query2("INSERT INTO privmsg (to_id, from_id, time_sent, subject, message, deleted_from, deleted_to) VALUES"
." ('".$user_to_notify['account']."', '".mysql_real_escape_string($from)."', '".mysql_real_escape_string($dateadded)."', '(".implode(', ', $groups).") ".mysql_real_escape_string($sub)."', '".mysql_real_escape_string($edited)."', 1, 0)");
} else {
// Multiple Groups; check for DE
if (in_array('DE', $groups)) {
// Send to DEM and DEAM, too
$users_to_notify=mysql_query2("SELECT DISTINCT account FROM user WHERE deleted=0 AND (DEM=1 OR DEAM=1)");
$recipient_total = mysql_num_rows($users_to_notify);
// Notify all found users
while ($user_to_notify=mysql_fetch_array($users_to_notify))
$groupnotify = mysql_query2("INSERT INTO privmsg (to_id, from_id, time_sent, subject, message, deleted_from, deleted_to) VALUES"
." ('".$user_to_notify['account']."', '".mysql_real_escape_string($from)."', '".mysql_real_escape_string($dateadded)."', '(".$group.") ".mysql_real_escape_string($sub)."', '".mysql_real_escape_string($edited)."', 1, 0)");
}

foreach($groups as $k=>$group) {
$groups[$k] = mysql_real_escape_string($group);
$users_to_notify=mysql_query2("SELECT DISTINCT account FROM user WHERE deleted=0 AND ( " . implode( '=1 OR ', $groups) . "=1)")
$recipient_total = mysql_num_rows($users_to_notify);
// Notify all found users
while ($user_to_notify=mysql_fetch_array($users_to_notify))
$groupnotify = mysql_query2("INSERT INTO privmsg (to_id, from_id, time_sent, subject, message, deleted_from, deleted_to) VALUES"
." ('".$user_to_notify['account']."', '".mysql_real_escape_string($from)."', '".mysql_real_escape_string($dateadded)."', '(".$group.") ".mysql_real_escape_string($sub)."', '".mysql_real_escape_string($edited)."', 1, 0)");
}
}

if (!$groupnotify) {
// database error
} else {
// sent ok
}
[/code]
Copy linkTweet thisAlerts:
@scragarJan 12.2009 — You missed the point, the for each is just to clean up anything nasty that might get entered, which is why I only indented the line under it. I used PHPs implode function to make the whole thing a single line(in fact, yourif(in_array('DE', $groups)){
//.....
}

section could probably be replaced with:
<i>
</i>if(in_array('DE', $groups))
array_push($groups, 'DEM', 'DEAM');
This would also reduce the chance of it sending a PM to someone twice.
Copy linkTweet thisAlerts:
@DanUKauthorJan 12.2009 — Ah, that array_push looks interesting, I just read up about it. A lot cleaner.

I'm still not really 100% on what you mean about that foreach I'm afraid (sorry)... I therefore looked at your original paste about this "group" part and noticed you didn't have that foreach beneath the initial $group, so I removed that.

I now have the following, but this time I get php errors, so I guess I'm not doing what you're trying to explain :o

[code=php]
// Note array_unique() here for unique accounts; no duplicates
$groups = !is_array($_POST['group']) ? $_POST['group'] : array_unique($_POST['group']);
$from = $_SESSION['account'];
// blah blah for the rest; the same

// Send to everyone
if (in_array('All', $groups)) {
// Find ALL accounts from * user
$users_to_notify=mysql_query2("SELECT * FROM user WHERE deleted=0");
$recipient_total = mysql_num_rows($users_to_notify);
// Notify all found users
while ($user_to_notify=mysql_fetch_array($users_to_notify))
$groupnotify = mysql_query2("INSERT INTO privmsg (to_id, from_id, time_sent, subject, message, deleted_from, deleted_to) VALUES"
." ('".$user_to_notify['account']."', '".mysql_real_escape_string($from)."', '".mysql_real_escape_string($dateadded)."', '(".implode(', ', $groups).") ".mysql_real_escape_string($sub)."', '".mysql_real_escape_string($edited)."', 1, 0)");
} else {
if(in_array('DE', $groups))
array_push($groups, 'DEM', 'DEAM');

foreach($groups as $k=>$group)
$groups[$k] = mysql_real_escape_string($group);

$users_to_notify=mysql_query2("SELECT DISTINCT account FROM user WHERE deleted=0 AND ( " . implode( '=1 OR ', $groups) . "=1)")
$recipient_total = mysql_num_rows($users_to_notify);
// Notify all found users
while ($user_to_notify=mysql_fetch_array($users_to_notify))
$groupnotify = mysql_query2("INSERT INTO privmsg (to_id, from_id, time_sent, subject, message, deleted_from, deleted_to) VALUES"
." ('".$user_to_notify['account']."', '".mysql_real_escape_string($from)."', '".mysql_real_escape_string($dateadded)."', '(".$group.") ".mysql_real_escape_string($sub)."', '".mysql_real_escape_string($edited)."', 1, 0)");
}

if (!$groupnotify) {
// database error
} else {
// sent ok
}
[/code]


Thanks for being so patient!
Copy linkTweet thisAlerts:
@scragarJan 12.2009 — Could you say what the errors are? It would be helpful to debuging and all.

And do you mind me asking why you are adding $group to the message, because of the for each $group will only contain the last used group, if you wanted to include the group(s) the users were contacted for you might have to build a more complex array, then loop though that array, which will be slower and more ram intensive. I think you could use implode(', ', $groups) to return a list of groups, that's about it.
Copy linkTweet thisAlerts:
@DanUKauthorJan 12.2009 — You're right about that implode, I had actually done that before, but I guess I pasted old code back over it. Thanks for noticing.

The error I get is:

Parse error: syntax error, unexpected T_VARIABLE

which is showing for the BOLD line below:

<i>
</i> $users_to_notify=mysql_query2("SELECT DISTINCT account FROM user WHERE deleted=0 AND ( " . implode( '=1 OR ', $groups) . "=1)")
[b] $recipient_total = mysql_num_rows($users_to_notify);[/b]
// Notify all found users
Copy linkTweet thisAlerts:
@scragarJan 12.2009 — Add a ;to the end of the line above it. :p
Copy linkTweet thisAlerts:
@DanUKauthorJan 12.2009 — *Does a Homer 'D'oh'*

Stupid me. Thanks, giving it a whirl now
Copy linkTweet thisAlerts:
@DanUKauthorJan 12.2009 — You're a star! It's working perfectly.

Thanks so much scragar.

Last question:

[code=php]
// Note array_unique() here for unique accounts; no duplicates
$groups = !is_array($_POST['group']) ? $_POST['group'] : array_unique($_POST['group']);
foreach($groups as $i=>$group) {
if(empty($group)) {
unset($groups[$i]);
} else {
$groups[$i] = htmlentities($group);
}
}
$from = $_SESSION['account'];
$sub = htmlentities($_POST['subject']);
$message = htmlentities($_POST['msg']);
[/code]


Is that foreach() needed, as it wasn't on your original paste about this "group" case. It's working fine with it, so I don't really want to do anything more ?
Copy linkTweet thisAlerts:
@scragarJan 12.2009 — The for each could be lost, I put it in for single usernames when I didn't know the input method, since the input method is a select box adding <span><code></CODE>around variable names should be sufficient protection:
<CODE lang="php">[code=php] $users_to_notify=mysql_query2("SELECT DISTINCT account FROM user WHERE deleted=0 AND (
" . implode( '=1 OR ', $groups) . "`=1)");[/code]And quicker, and safer, since nasty little queries can't get submitted.
Copy linkTweet thisAlerts:
@DanUKauthorJan 12.2009 — I just tried that. I got a SQL error as it tried to submit:

SELECT DISTINCT account FROM user WHERE deleted=0 AND ( JU=1 OR SU=1 OR ``=1)

with that foreach it seemed to work fine; maybe that was safer? ?
Copy linkTweet thisAlerts:
@scragarJan 12.2009 — Leave the for each in, but keep the `s, they are better for security than you think, it's just than an empty element is getting through now, should really try to find where that is comming from and fix it there, still. Working > being fixed :p
Copy linkTweet thisAlerts:
@DanUKauthorJan 12.2009 — Okay, yep it's working fine with both there. ??
Copy linkTweet thisAlerts:
@DanUKauthorJan 14.2009 — Hi scragar, hope you're doing okay.

Any luck pinpointing where that empty element is coming from? I haven't been able to pinpoint it either to replicate it.

However... with that other code it seems absolutely fine - is it fine to just leave as it is?

Thanks again ?
Copy linkTweet thisAlerts:
@scragarJan 14.2009 — Leave it as it is, I have a bit of a tendency to break things when I try to fix things that aren't problems(it happens a lot :p That why I use classes as much as I can now, with classes it means I can dump everything in one go and get a really good idea of what's happening if anything looks to be broken).
Copy linkTweet thisAlerts:
@DanUKauthorJan 14.2009 — Quick reply ?

Okay, thanks. It seems to be working fine so I won't worry.
×

Success!

Help @DanUK spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 5.4,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @Yussuf4331,
tipped: article
amount: 1000 SATS,

tipper: @darkwebsites540,
tipped: article
amount: 10 SATS,

tipper: @Samric24,
tipped: article
amount: 1000 SATS,
)...