ljr/livejournal/htdocs/interests.bml

461 lines
18 KiB
Plaintext
Raw Normal View History

2019-02-05 21:49:12 +00:00
<?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 .= "&nbsp;<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}) . "&nbsp;";
$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}) . "&nbsp;";
$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}) . "&nbsp;";
$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?>