461 lines
18 KiB
Plaintext
Executable File
461 lines
18 KiB
Plaintext
Executable File
<?page
|
|
title=><?_ml .title _ml?>
|
|
head=><?_code return LJ::robot_meta_tags(); _code?>
|
|
body<=
|
|
|
|
<?_code
|
|
{
|
|
use strict;
|
|
use vars qw(%GET %POST);
|
|
|
|
LJ::set_active_crumb('searchinterests');
|
|
|
|
return "<?badinput?>" unless LJ::text_in(\%GET) && LJ::text_in(\%POST);
|
|
|
|
my $did_post = LJ::did_post();
|
|
|
|
my $remote = LJ::get_remote();
|
|
|
|
my $table = sub { $_[0]->{'journaltype'} eq 'C' ? 'comminterests' : 'userinterests' };
|
|
|
|
if (!$did_post && $GET{'view'} eq "popular") {
|
|
return $ML{'.popular.disabled'} if $LJ::DISABLED{'interests-popular'};
|
|
my $ret = "";
|
|
$ret .= "<?h1 $ML{'.popular.head'} h1?><?p $ML{'.popular.text'} p?>";
|
|
|
|
my $dbr = LJ::get_db_reader();
|
|
my $sth = $dbr->prepare("SELECT statkey, statval FROM stats WHERE ".
|
|
"statcat=? ORDER BY statval DESC, statkey ASC");
|
|
$sth->execute('pop_interests');
|
|
return "Sorry, interest data currently unavailable." unless $sth->rows();
|
|
|
|
$ret .= "<p><table><tr><td><b>$ML{'.interest'}</b></td><td><b>$ML{'.count'}</b></td></tr>";
|
|
while (my ($int, $count) = $sth->fetchrow_array)
|
|
{
|
|
next if ($count == 1);
|
|
LJ::text_out(\$int);
|
|
my $eint = LJ::eurl($int);
|
|
$ret .= "<tr><td><a href=\"/interests.bml?int=$eint\">$int</a></td><td>$count</td></tr>";
|
|
}
|
|
$ret .= "</table>";
|
|
return $ret;
|
|
}
|
|
|
|
if ((!$did_post && $GET{'mode'} eq "add" && $GET{'intid'}) ||
|
|
($did_post && $POST{'mode'} eq "add" && $POST{'intid'})) {
|
|
|
|
my $intid = $did_post ? $POST{'intid'}+0 : $GET{'intid'}+0;
|
|
|
|
my $ret;
|
|
unless ($remote) {
|
|
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.add.mustlogin'} p?>";
|
|
return $ret;
|
|
}
|
|
|
|
# force them to either come from the interests.bml page, or have posted the request.
|
|
# if both fail, ask them to confirm with a post form.
|
|
|
|
my $dbr = LJ::get_db_reader();
|
|
|
|
unless ($did_post || BML::get_client_header('Referer') =~ /^\Q$LJ::SITEROOT\E\/interests\.bml\?/)
|
|
{
|
|
my ($int) = $dbr->selectrow_array("SELECT interest FROM interests WHERE intid=?", undef, $intid);
|
|
LJ::text_out(\$int);
|
|
|
|
$ret .= "<?h1 $ML{'.add.confirm.head'} h1?>";
|
|
$ret .= "<?p " . BML::ml(".add.confirm.text", {'interest' => $int});
|
|
$ret .= "<form method='post' action='interests.bml'><div align='center'>";
|
|
$ret .= LJ::html_hidden('mode' => 'add', 'intid' => $intid);
|
|
$ret .= LJ::html_submit(undef, BML::ml(".add.btn.text", {'interest' => $int}));
|
|
$ret .= "</div></form> p?>";
|
|
return $ret;
|
|
}
|
|
|
|
my $rints = LJ::get_interests($remote);
|
|
my $count = scalar(@$rints);
|
|
|
|
if ($count >= 150) {
|
|
$ret .= "<?h1 $ML{'.add.toomany.head'} h1?><?p " .BML::ml(".add.toomany.text", {'maxinterests' => "150"}) ." p?>";
|
|
return $ret;
|
|
}
|
|
|
|
my $dbh = LJ::get_db_writer();
|
|
my $uitable = $table->($remote);
|
|
$dbh->do("INSERT INTO $uitable (userid, intid) VALUES (?, ?)",
|
|
undef, $remote->{'userid'}, $intid);
|
|
LJ::memcache_kill($remote, "intids");
|
|
unless ($dbh->err) {
|
|
$dbh->do("UPDATE interests SET intcount=intcount+1 WHERE intid=?", undef, $intid);
|
|
}
|
|
|
|
# if a community, remove any old rows from userinterests
|
|
if ($remote->{'journaltype'} eq 'C') {
|
|
$dbh->do("DELETE FROM userinterests WHERE userid=?", undef, $remote->{'userid'});
|
|
}
|
|
|
|
$ret .= "<?h1 $ML{'.add.added.head'} h1?><?p $ML{'.add.added.text'} p?>";
|
|
return $ret;
|
|
}
|
|
|
|
if (!$did_post && $GET{'mode'} eq "findsim_do") {
|
|
return $ML{'error.tempdisabled'} if $LJ::DISABLED{'interests-findsim'};
|
|
|
|
return $ML{'.findsim_do.account.notallowed'} unless LJ::get_cap($remote, "findsim");
|
|
|
|
my $ret = "";
|
|
my $u = LJ::load_user($GET{'user'});
|
|
return "<?h1 $ML{'Error'} h1?><?p $ML{'error.username_notfound'} p?>" unless $u;
|
|
|
|
my @ints;
|
|
my %intcount;
|
|
my $dbr = LJ::get_db_reader();
|
|
my $sth = $dbr->prepare("SELECT i.intid, i.intcount FROM userinterests ui, interests i ".
|
|
"WHERE ui.userid=? AND ui.intid=i.intid");
|
|
$sth->execute($u->{'userid'});
|
|
while (my ($intid, $count) = $sth->fetchrow_array) {
|
|
push @ints, $intid;
|
|
$intcount{$intid} = $count || 1;
|
|
}
|
|
unless (@ints) {
|
|
my $msg = BML::ml('.findsim_do.notdefined', { 'user' => LJ::ljuser($u) });
|
|
return "<?h1 $ML{'Error'} h1?><?p $msg p?>";
|
|
}
|
|
|
|
my %pt_count;
|
|
my %pt_weight;
|
|
foreach my $int (@ints) {
|
|
# the magic's in this limit clause. that's what makes this work. perfect
|
|
# results? no. but who cares if somebody that lists "music" or "women"
|
|
# doesn't get an extra point towards matching you. we care about more unique interests.
|
|
my $sth = $dbr->prepare("SELECT userid FROM userinterests WHERE intid=? LIMIT 500");
|
|
$sth->execute($int);
|
|
while (my $uid = $sth->fetchrow_array) {
|
|
next if $uid == $u->{'userid'};
|
|
$pt_weight{$uid} += (1 / log($intcount{$int}+1));
|
|
$pt_count{$uid}++;
|
|
}
|
|
}
|
|
|
|
my %magic; # balanced points
|
|
foreach (keys %pt_count) {
|
|
$magic{$_} = $pt_weight{$_}*10 + $pt_count{$_};
|
|
}
|
|
|
|
my @matches = sort { $magic{$b} <=> $magic{$a} } keys %magic;
|
|
if (@matches > 150) { @matches = @matches[0..149]; }
|
|
my $sth = $dbr->prepare("SELECT userid, user FROM useridmap WHERE userid IN (" . join(",",@matches) . ")");
|
|
$sth->execute;
|
|
my %username;
|
|
while (my ($id, $name) = $sth->fetchrow_array) {
|
|
$username{$id} = $name;
|
|
}
|
|
|
|
unless (@matches) {
|
|
return "<?h1 $ML{'.findsim_do.nomatch.head'} h1?><?p " .BML::ml(".findsim_do.nomatch.text", {'user' => LJ::ljuser($u)}) ." p?>";
|
|
}
|
|
|
|
$ret .= "<?h1 $ML{'.findsim_do.similar.head'} h1?><?p " .BML::ml(".findsim_do.similar.text", {'user' => LJ::ljuser($u)}) ." p?>";
|
|
|
|
$ret .= "<p><table cellpadding='3'><tr valign='bottom'><td><b>#</b></td><td width='250'><b>$ML{'User'}</b></td><td><b>$ML{'.findsim_do.magic'}</b></td></tr>";
|
|
my $count;
|
|
foreach my $uid (@matches)
|
|
{
|
|
$count++;
|
|
$ret .= "<tr><td>$count</td><td>";
|
|
$ret .= LJ::ljuser($username{$uid});
|
|
$ret .= sprintf("</td><td>%.3f</td></tr>", $magic{$uid});
|
|
}
|
|
$ret .= "</table></p>";
|
|
|
|
$ret .= "<?h1 $ML{'.findsim_do.magic.head'} h1?><?p $ML{'.findsim_do.magic.text'} p?>";
|
|
return $ret;
|
|
}
|
|
|
|
if (!$did_post && $GET{'mode'} eq "enmasse")
|
|
{
|
|
return LJ::bad_input($ML{'.error.enmasse.mustlogin'}) unless $remote;
|
|
|
|
my $authas = $GET{'authas'} || $remote->{'user'};
|
|
my $u = LJ::get_authas_user($authas);
|
|
return LJ::bad_input(BML::ml('.error.enmasse.noaccess', {'user' => LJ::ljuser($authas)})) unless $u;
|
|
|
|
my $altauthas = $remote->{'user'} ne $u->{'user'};
|
|
my $getextra = $altauthas ? "?authas=$u->{'user'}" : '';
|
|
|
|
my $userid = $u->{'userid'};
|
|
my $username = $u->{'user'};
|
|
my $fromu = LJ::load_user($GET{'fromuser'} || $username);
|
|
|
|
my %uint;
|
|
|
|
my %fromint;
|
|
my $fints = LJ::get_interests($fromu);
|
|
foreach (@$fints) {
|
|
$fromint{$_->[1]} = $_->[0]+0;
|
|
}
|
|
|
|
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.nointerests'} p?>" unless %fromint;
|
|
|
|
my $ret = "<?p <form method='get' action='interests.bml'>";
|
|
$ret .= LJ::html_hidden(mode => 'enmasse', fromuser => $fromu->{'user'});
|
|
$ret .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'} });
|
|
$ret .= "</form> p?><form method='post' action='interests.bml$getextra'>";
|
|
$ret .= "<?h1 $ML{'.enmasse.header'} h1?><?p ";
|
|
|
|
if ($u->{'userid'} == $fromu->{'userid'}) {
|
|
%uint = %fromint;
|
|
$ret .= $ML{'.enmasse.body.you'};
|
|
} else {
|
|
my $in = join (",", map { $fromint{$_} } keys %fromint);
|
|
|
|
my $uints = LJ::get_interests($u);
|
|
foreach (@$uints) {
|
|
$uint{$_->[1]} = $_->[0];
|
|
}
|
|
|
|
if ($altauthas) {
|
|
$ret .= BML::ml('.enmasse.body.other_authas', { 'user' => LJ::ljuser($fromu),
|
|
'target' => LJ::ljuser($u) });
|
|
} else {
|
|
$ret .= BML::ml('.enmasse.body.other', { 'user' => LJ::ljuser($fromu) });
|
|
}
|
|
}
|
|
|
|
$ret .= " p?><div style='margin-left: 40px; margin-top: 20px;'>";
|
|
$ret .= "<table cellpadding='0' cellspacing='0' border='0' width='100%'>";
|
|
my @fromintsorted = sort keys %fromint;
|
|
my $cols = 3;
|
|
my $rows = int((scalar(@fromintsorted) + $cols - 1) / $cols);
|
|
for (my $i = 0; $i < $rows; $i++) {
|
|
$ret .= "<tr valign='middle'>";
|
|
for (my $j = 0; $j < $cols; $j++) {
|
|
my $index = $rows * $j + $i;
|
|
if ($index < scalar(@fromintsorted)) {
|
|
my $checked = $uint{$fromintsorted[$index]} ? 1 : undef;
|
|
$ret .= "<td align='left' nowrap='nowrap'>";
|
|
$ret .= LJ::html_check({name => "int_$fromint{$fromintsorted[$index]}",
|
|
id => "int_$fromint{$fromintsorted[$index]}",
|
|
selected => $checked,
|
|
value => 1});
|
|
my $bold1 = $checked ? "<strong>" : "";
|
|
my $bold2 = $checked ? "</strong>" : "";
|
|
$ret .= " <label for='int_$fromint{$fromintsorted[$index]}'>";
|
|
$ret .= "$bold1$fromintsorted[$index]$bold2</label></td>";
|
|
} else {
|
|
$ret .= "<td></td>";
|
|
}
|
|
}
|
|
$ret .= "</tr>";
|
|
}
|
|
$ret .= "</table></div>";
|
|
|
|
$ret .= LJ::html_hidden('mode', 'enmasse_do');
|
|
$ret .= LJ::html_hidden('fromuser', $fromu->{'user'});
|
|
$ret .= LJ::html_hidden('allintids', join (",", values %fromint));
|
|
|
|
$ret .= "<?h1 $ML{'.finished.header'} h1?><?p $ML{'.finished.about'} p?><?standout ";
|
|
$ret .= LJ::html_submit(undef, $ML{'.finished.save_button'}) . " standout?></form>";
|
|
return $ret;
|
|
}
|
|
|
|
if ($did_post && $POST{'mode'} eq "enmasse_do") {
|
|
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.enmasse.mustlogin'} p?>" unless $remote;
|
|
|
|
my $authas = $GET{'authas'} || $remote->{'user'};
|
|
my $u = LJ::get_authas_user($authas);
|
|
return LJ::bad_input($ML{'.error.noauth'}) unless $u;
|
|
|
|
my $uitable = $table->($u);
|
|
my %uint;
|
|
my $intcount = 0;
|
|
|
|
my $uints = LJ::get_interests($u);
|
|
foreach (@$uints) {
|
|
$uint{$_->[0]} = $_->[1]; # uint{intid} = interest
|
|
$intcount++;
|
|
}
|
|
|
|
my @fromints = map { $_+0 } split (/\s*,\s*/, $POST{'allintids'});
|
|
my @todel;
|
|
my @toadd;
|
|
foreach my $fromint (@fromints) {
|
|
next unless $fromint > 0; # prevent adding zero or negative intid
|
|
push (@todel, $fromint) if $uint{$fromint} && !$POST{'int_'.$fromint};
|
|
push (@toadd, $fromint) if !$uint{$fromint} && $POST{'int_'.$fromint};
|
|
}
|
|
my ($deleted, $added, $toomany) = (0, 0, 0);
|
|
if (@todel) {
|
|
my $intid_in = join(",", @todel);
|
|
my $dbh = LJ::get_db_writer();
|
|
$dbh->do("DELETE FROM $uitable WHERE userid=? AND intid IN ($intid_in)",
|
|
undef, $u->{'userid'});
|
|
$dbh->do("UPDATE interests SET intcount=intcount-1 WHERE intid IN ($intid_in)");
|
|
$deleted = 1;
|
|
}
|
|
if (@toadd) {
|
|
if ($intcount + scalar @toadd > 150) {
|
|
$toomany = 1;
|
|
} else {
|
|
my $dbh = LJ::get_db_writer();
|
|
my $sqlp = "(?,?)" . (",(?,?)" x (scalar(@toadd) - 1));
|
|
my @bindvars = map { ($u->{'userid'}, $_) } @toadd;
|
|
$dbh->do("REPLACE INTO $uitable (userid, intid) VALUES $sqlp", undef, @bindvars);
|
|
|
|
my $intid_in = join(",", @toadd);
|
|
$dbh->do("UPDATE interests SET intcount=intcount+1 WHERE intid IN ($intid_in)");
|
|
$added = 1;
|
|
}
|
|
}
|
|
|
|
# if a community, remove any old rows from userinterests
|
|
if ($u->{'journaltype'} eq 'C') {
|
|
my $dbh = LJ::get_db_writer();
|
|
$dbh->do("DELETE FROM userinterests WHERE userid=?", undef, $u->{'userid'});
|
|
}
|
|
|
|
my $ret = "<?h1 $ML{'.results.header'} h1?><?p ";
|
|
if ($deleted) {
|
|
$ret .= $added ? $ML{'.results.both'}
|
|
: $toomany ? BML::ml('.results.del_and_toomany', {'intcount' => 150})
|
|
: $ML{'.results.deleted'};
|
|
} else {
|
|
$ret .= $added ? $ML{'.results.added'}
|
|
: $toomany ? BML::ml('.results.toomany', {'intcount' => 150})
|
|
: $ML{'.results.nothing'};
|
|
}
|
|
|
|
$ret .= " p?><?p " . BML::ml('.results.message', { 'url' => '/userinfo.bml?user=' . $u->{'user'} });
|
|
$ret .= " " . BML::ml('.results.goback',
|
|
{ 'url' => '/userinfo.bml?user=' . LJ::eurl($POST{'fromuser'}),
|
|
'user' => LJ::ljuser($POST{'fromuser'}),
|
|
}) if ($POST{'fromuser'} ne "" && $POST{'fromuser'} ne $u->{'user'});
|
|
$ret .= " p?>";
|
|
LJ::memcache_kill($u, "intids");
|
|
return $ret;
|
|
}
|
|
|
|
if (!$did_post && ($GET{'intid'} || $GET{'int'})) {
|
|
my $sth;
|
|
my $dbr = LJ::get_db_reader();
|
|
my ($interest, $intid, $intcount);
|
|
if ($GET{'intid'}) {
|
|
($interest, $intid, $intcount) = $dbr->selectrow_array("SELECT interest, intid, intcount
|
|
FROM interests WHERE intid=?", undef, $GET{'intid'});
|
|
} else {
|
|
($interest, $intid, $intcount) = $dbr->selectrow_array("SELECT interest, intid, intcount
|
|
FROM interests WHERE interest=?", undef, $GET{'int'});
|
|
}
|
|
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.findsim_do.intnotfound'} p?>" unless $interest;
|
|
$intid += 0;
|
|
|
|
my $ret = "";
|
|
|
|
### hook
|
|
LJ::run_hooks("interests_bml", {
|
|
'intid' => $intid,
|
|
'int' => $interest,
|
|
'ret' => \$ret,
|
|
});
|
|
|
|
### communities
|
|
my $LIMIT = 500;
|
|
|
|
unless ($LJ::DISABLED{'interests-community'}) {
|
|
my @uids;
|
|
$sth = $dbr->prepare("SELECT userid FROM comminterests WHERE intid=? LIMIT $LIMIT");
|
|
$sth->execute($intid);
|
|
push @uids, $_ while $_ = $sth->fetchrow_array;
|
|
my $us = LJ::load_userids(@uids);
|
|
my @cl = grep { $_->{statusvis} eq "V" } values %$us;
|
|
@cl = sort { $a->{user} cmp $b->{user} } @cl;
|
|
my $count = @cl;
|
|
|
|
my $list;
|
|
foreach (@cl) {
|
|
my $name = $_->{name};
|
|
LJ::text_out(\$name);
|
|
$list .= "<li>" . LJ::ljuser($_) . " - " . LJ::ehtml($name) . "</li>";
|
|
}
|
|
|
|
if (@cl) {
|
|
my $matchcount = BML::ml(@cl == 1 ? ".match" : ".matches", {'count' => $count });
|
|
$ret .= "<?h1 $ML{'.communities.head'} h1?><?p " .
|
|
BML::ml(".communities.text", {'interest' => $interest}) ." p?>";
|
|
$ret .= "<p><b>$matchcount</b><ul>$list</ul></p>";
|
|
}
|
|
}
|
|
|
|
##### users
|
|
|
|
$ret .= "<?h1 $ML{'.users.head'} h1?><?p " . BML::ml(".users.text", {'interest' => $interest});
|
|
if ($remote) {
|
|
$ret .= " " . BML::ml(".addint", {'qintid' => $intid});
|
|
}
|
|
$ret .= " $ML{'.morestuff'} p?>";
|
|
|
|
my @uids;
|
|
$sth = $dbr->prepare("SELECT userid FROM userinterests WHERE intid=? LIMIT $LIMIT");
|
|
$sth->execute($intid);
|
|
push @uids, $_ while $_ = $sth->fetchrow_array;
|
|
my $us = LJ::load_userids(@uids);
|
|
my @ul = grep { $_->{statusvis} eq "V" && $_->{journaltype} ne "C" } values %$us;
|
|
@ul = sort { $a->{user} cmp $b->{user} } @ul;
|
|
my $count = @ul;
|
|
|
|
my $list;
|
|
foreach (@ul) {
|
|
my $name = $_->{name};
|
|
LJ::text_out(\$name);
|
|
$list .= "<li>" . LJ::ljuser($_) . " - " . LJ::ehtml($name) . "</li>";
|
|
}
|
|
|
|
my $matchcount = BML::ml(@ul == 1 ? ".match" : ".matches", { 'count' => $count });
|
|
$ret .= "<p><b>$matchcount</b><ul>$list</ul></p>";
|
|
|
|
return $ret;
|
|
}
|
|
|
|
my $ret = "";
|
|
$ret .= "<?h1 $ML{'.interests.head'} h1?><?p $ML{'.interests.text'} p?>";
|
|
$ret .= "<table cellspacing='5' style='margin-top: 10px; margin-left: 30px; margin-bottom: 10px;'>";
|
|
|
|
unless ($LJ::DISABLED{'interests-popular'}) {
|
|
$ret .= "<tr valign='top'><td colspan='2'>";
|
|
$ret .= "<a href=\"interests.bml?view=popular\">$ML{'.interests.viewpop'}</a></td></tr>";
|
|
}
|
|
|
|
$ret .= "<tr valign='top'><td align='left'>$ML{'.interested.in'}</td>";
|
|
$ret .= "<td><form method='get' action='interests.bml'>";
|
|
$ret .= LJ::html_text({name => 'int', size => 20}) . " ";
|
|
$ret .= LJ::html_submit(undef, $ML{'.interested.btn.find'});
|
|
$ret .= "</form></td></tr>";
|
|
|
|
if (!$LJ::DISABLED{'interests-findsim'} && $remote && LJ::get_cap($remote, "findsim")) {
|
|
$ret .= "<tr valign='top'><td>$ML{'.interests.findsim'}</td><td><form method='get' action='interests.bml'>";
|
|
$ret .= LJ::html_hidden('mode', 'findsim_do');
|
|
$ret .= LJ::html_text({name => 'user', value => $remote->{'user'}, size => 20}) . " ";
|
|
$ret .= LJ::html_submit(undef, $ML{'.interested.btn.find'});
|
|
$ret .= "</form></td></tr>";
|
|
}
|
|
|
|
$ret .= "<tr valign='top'><td>$ML{'.enmasse.intro'}</td>";
|
|
$ret .= "<td><form method='get' action='interests.bml'>";
|
|
$ret .= LJ::html_text({name => 'fromuser', size => 20}) . " ";
|
|
$ret .= LJ::html_submit(undef, $ML{'.enmasse.btn'});
|
|
$ret .= LJ::html_hidden('mode', 'enmasse');
|
|
$ret .= "</form></td></tr>";
|
|
|
|
$ret .= "</table>";
|
|
$ret .= $ML{'.nointerests.text'};
|
|
|
|
return $ret;
|
|
}
|
|
_code?>
|
|
|
|
<=body
|
|
page?><?_c <LJDEP>
|
|
link: htdocs/interests.bml, htdocs/editinfo.bml
|
|
post: htdocs/interests.bml
|
|
form: htdocs/interests.bml
|
|
</LJDEP> _c?>
|