This commit is contained in:
2019-02-06 00:49:12 +03:00
commit 8dbb1bb605
4796 changed files with 506072 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
<h1>Sorry</h1>
<p>We're currently working on something. The site will be back up shortly.
<!--
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-->

15
livejournal/htdocs/_config.bml Executable file
View File

@@ -0,0 +1,15 @@
LookRoot $LJHOME/cgi-bin/bml/scheme
DefaultScheme bluewhite
DefaultLanguage en
IncludePath inc/
AllowOldSyntax 0
ExtraConfig _config-local.bml, $LJHOME/cgi-bin/lj-bml-init.pl
AllowCode 1
AllowTemplateCode 1
SubConfig: guide/, clients/, files/
AllowCode 0

View File

@@ -0,0 +1,94 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
{
use strict;
use vars qw(%GET %POST);
LJ::set_active_crumb('acctstatus');
my $remote = LJ::get_remote();
return LJ::bad_input($ML{'error.noremote'})
unless $remote;
my $authas = $GET{'authas'} || $remote->{'user'};
my $u = LJ::get_authas_user($authas);
return LJ::bad_input($ML{'error.invalidauth'})
unless $u;
# visibility status name mappings
my %statusvis = ( "V" => "$ML{'.journalstatus.select.activated'}",
"D" => "$ML{'.journalstatus.select.deleted'}" );
if ($u->{'statusvis'} eq "S") {
%statusvis = ("S" => "$ML{'.journalstatus.select.suspended'}");
}
# process form submission
if (LJ::did_post() && LJ::check_referer('/accountstatus.bml')) {
my $ok = sub {
return "<?h1 $ML{'.header.success'} h1?>\n<?p " . $_[0] . " p?>";
};
my $dberr = sub {
return "<?h1 $ML{'Error'} h1?>\n<?p " . $_[0] . " p?>";
};
# are they suspended?
return LJ::bad_input($ML{'.error.nochange.suspend'})
if $u->{'statusvis'} eq 'S';
# are they expunged?
return LJ::bad_input($ML{'.error.nochange.expunged'})
if $u->{'statusvis'} eq 'X';
# invalid statusvis
return LJ::bad_input($ML{'.error.invalid'})
unless $POST{'statusvis'} eq 'D' || $POST{'statusvis'} eq 'V';
# no need to change?
return $ok->(BML::ml('.message.nochange', {'statusvis' => $statusvis{$POST{'statusvis'}} }))
if $u->{'statusvis'} eq $POST{'statusvis'};
# do update
my $res = LJ::update_user($u, { 'statusvis' => $POST{'statusvis'},
'raw' => 'statusvisdate=NOW()' });
# error updating?
return $dberr->($ML{'.error.db'})
unless $res;
# success
return $ok->(BML::ml('.message.success', {'statusvis' => $statusvis{$POST{'statusvis'}} }));
}
# update form
my $ret;
# authas switcher form
$ret .= "<form method='get' action='accountstatus.bml'>\n";
$ret .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'} }) . "\n";
$ret .= "</form>\n\n";
my $getextra = "?authas=$authas" unless $authas eq $remote->{'user'};
$ret .= "<form method='post' action='accountstatus.bml$getextra'>\n";
$ret .= "<?h1 $ML{'.journalstatus.head'} h1?><?p $ML{'.journalstatus.about'} p?>\n";
$ret .= "<div style='margin-left: 30px'>$ML{'.journalstatus.select.head'}\n";
$ret .= LJ::html_select({ 'name' => 'statusvis', 'selected' => $u->{'statusvis'} },
map { $_, $statusvis{$_} }
reverse sort keys %statusvis) . "\n";
$ret .= LJ::html_submit(undef, $ML{'.btn.status'},
{ 'disabled' => $u->{'statusvis'} eq 'S' }) . "\n";
$ret .= "</div>\n</form>\n";
return $ret;
}
_code?>
<=body
page?>

View File

@@ -0,0 +1,104 @@
<?_code
use strict;
use vars qw(%FORM);
my ($sth, $ret);
my $mode = $FORM{'mode'};
my $remote = LJ::get_remote();
$mode ||= $FORM{'user'} ? "viewuser" : "intro";
my $user = LJ::canonical_username($FORM{'user'});
my $u;
$u = LJ::load_user($user) if $user;
if ($mode eq "intro")
{
$ret .= "<h1>capability class management</h1>\n";
$ret .= "<form method='get'>";
$ret .= "Modify capabilities for user: <input name='user' size='15'> <input type='submit' value=\"Load\">";
$ret .= "</form>";
return $ret;
}
if ($mode eq "save")
{
return "<b>Error:</b> requires post"
unless (LJ::did_post());
return"<b>Error:</b> You don't have access to change a user's capability class."
unless (LJ::check_priv($remote, "admin", "*"));
unless ($u) {
$ret .= "Unknown user.\n";
return $ret;
}
my @cap_add = ();
my @cap_del = ();
my $newcaps = $u->{caps};
foreach my $n (sort { $a <=> $b } keys %LJ::CAP) {
if ($FORM{"class_$n"}) {
push @cap_add, $n;
$newcaps |= (1 << $n);
} else {
push @cap_del, $n;
$newcaps &= ~(1 << $n);
}
}
# note which caps were changed and log $logmsg to statushistory
my $add_txt = join(",", @cap_add);
my $del_txt = join(",", @cap_del);
LJ::statushistory_add($u->{'userid'}, $remote->{'userid'},
"capedit", "add: $add_txt, del: $del_txt\n");
LJ::modify_caps($u, \@cap_add, \@cap_del)
or return"<b>Error:</b> Unable to modify caps.";
# $u->{caps} is now updated in memory for later in this request
$u->{caps} = $newcaps;
$ret .= "Saved.";
$mode = "viewuser";
}
if ($mode eq "viewuser")
{
$ret .= "<h1><a href='capedit.bml'>&lt;&lt;</a> edit user '$user'</h1>\n";
unless ($u) {
$ret .= "Unknown user.\n";
return $ret;
}
$ret .= "<form method='post'>";
$ret .= "<input type='hidden' name='mode' value='save'>\n";
$ret .= "<input type='hidden' name='user' value='$user'>\n";
foreach my $n (sort { $a <=> $b } keys %LJ::CAP)
{
my $on = ($u->{'caps'}+0) & (1 << $n);
my $checked = $on ? " checked='1'" : "";
$ret .= "<p><input type='checkbox' name='class_$n' value='1' id='class_$n' $checked> ";
my $name = $LJ::CAP{$n}->{'_name'} || "Unnamed capability class \#$n";
if ($on) { $ret .= "<b>"; }
$ret .= "<label for='class_$n'>$name</label>";
if ($on) { $ret .= "</b>"; }
}
$ret .= "<p><input type='submit' value='Save'>\n";
$ret .= "</form>";
return $ret;
}
return "Unknown mode.";
_code?><?_c <LJDEP>
lib: cgi-bin/ljlib.pl
form: htdocs/admin/capedit.bml
post: htdocs/admin/capedit.bml
</LJDEP _c?>

View File

@@ -0,0 +1,47 @@
<?page
title=>Cluster Status
body<=
<?_code
{
use strict;
my $remote = LJ::get_remote();
return "<?needlogin?>" unless $remote;
return "<?h1 Error h1?><?p You do not have the necessary privilege (supporthelp) to use this page. p?>"
unless LJ::check_priv($remote, 'supporthelp');
my $ret;
foreach my $cid (@LJ::CLUSTERS) {
my $name = LJ::get_cluster_description($cid) || 'no name';
$ret .= "<b>$name</b>: ";
my $check = 0;
if ($LJ::READONLY_CLUSTER{$cid}) {
$ret .= "<strong>read-only for all users</strong>";
} elsif ($LJ::READONLY_CLUSTER_ADVISORY{$cid} eq 'when_needed') {
$ret .= "<strong>read-only for free users during load</strong>",
} elsif ($LJ::READONLY_CLUSTER_ADVISORY{$cid}) {
$ret .= "<strong>read-only for free users</strong>";
} else {
$ret .= "no known issues";
$check = 1;
}
if ($check) {
my $dbcm = LJ::get_cluster_master($cid);
if ($dbcm) {
$ret .= "; available";
} else {
$ret .= "; <span style='color: red;'>unavailable</a>";
}
}
$ret .= "<br />";
}
return $ret;
}
_code?>
<=body
page?>

View File

@@ -0,0 +1,104 @@
<html>
<head><title>Admin Console</title></head>
<body>
<?_code
use strict;
use vars qw(%POST %cmd);
my ($ret, $sth);
my $commands = $POST{'commands'};
my $remote = LJ::get_remote();
if ($commands)
{
unless (LJ::did_post()) {
return "<b>Error:</b> requires post.";
}
if ($remote && $remote->{'user'} ne $POST{'remoteuser'}) {
return "<b>Error:</b> invalid user.";
}
$ret .= "[ <A HREF=\"./\">console</A> | <A HREF=\"reference.bml\">reference</A> ]<P>";
foreach my $cmd (split(/\n/, $commands))
{
my @args = LJ::Con::parse_line($cmd);
next unless @args;
my $first = 1;
$ret .= "<P><TABLE BORDER=1 CELLPADDING=5><TR>";
foreach (@args) {
my $arg = BML::eall($_);
if ($first) {
$ret .= "<TD><B>$arg</B></TD>";
$first = 0;
} else {
$ret .= "<TD>$arg</TD>";
}
}
$ret .= "</TR></TABLE>";
my @output;
my $rv;
# TODO: make the entire console library not take $db args.
my $dbh = LJ::get_db_writer();
$rv = LJ::Con::execute($dbh, $remote, \@args, \@output);
unless ($rv) { $ret .= "<P><B><FONT COLOR=#FF0000>Failed!</FONT></B>"; }
if (@output) {
$ret .= "<PRE><B>";
foreach my $line (@output) {
my $color = "#000000";
if ($line->[0] eq "error") {
$color = "#FF0000";
}
if ($line->[0] eq "info") {
$color = "#008800";
}
$ret .= "<FONT COLOR=$color>".LJ::eall($line->[1])."</FONT>\n";
}
$ret .= "</B></PRE>";
}
}
$ret .= "<form method=post><p>";
$ret .= "<tt>enter commands:</tt><br>";
$ret .= LJ::html_hidden('remoteuser', $remote->{'user'}) if $remote;
$ret .= "<textarea name=commands rows=3 cols=60 wrap=off></textarea> ";
$ret .= "<input type=submit value=\"execute\"></form>\n";
return $ret;
}
else
{
$ret .= "[ console | <A HREF=\"reference.bml\">reference</A> ]<P>";
$ret .= "<FORM METHOD=POST>";
$ret .= LJ::html_hidden('remoteuser', $remote->{'user'}) if $remote;
$ret .= "<TABLE WIDTH=400><TR VALIGN=BOTTOM>";
$ret .= "<TD><IMG SRC=\"$LJ::IMGPREFIX/nerd_small.jpg\" WIDTH=167 HEIGHT=169 HSPACE=2 VSPACE=2></TD>";
$ret .= "<TD><B><TT>command console.</TT></B>";
$ret .= "<P>welcome to the livejournal console. from here administrators can do administrative type things. you will forget the commands, so there is a <A HREF=\"reference.bml\">reference</A>.</TD>";
$ret .= "</TR>";
$ret .= "<TR><TD COLSPAN=2>";
$ret .= "<P><tt>enter commands:</tt><BR>";
$ret .= "<TEXTAREA NAME=commands ROWS=10 COLS=60 WRAP=OFF></TEXTAREA></TD></TR>\n";
$ret .= "<TR><TD COLSPAN=2 ALIGN=RIGHT><INPUT TYPE=SUBMIT VALUE=\"execute\"></P></TD></TR></TABLE></FORM>\n";
return $ret;
}
_code?>
</body>
</html>
<?_c <LJDEP>
lib: cgi-bin/console.pl, cgi-bin/ljlib.pl
link: htdocs/admin/console/reference.bml
post: htdocs/admin/console/index.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,49 @@
<html><head><title>Console Reference</title></head>
<body>
[ <a href="./">console</a> | reference ]
<P><B>Grammar</B>
<BR>Think of this like a DOS or <tt>bash</tt> prompt. The first word is a command. Every word after that is an argument to that command. Every command has a different number of required and optional parameters. White space delimits arguments. If you need a space in an argument, put double quotes around the whole thing. If you need double quotes and spaces in an argument, escape the quote with a backslash (\) first. If you need to do a backslash, escape that with a backslash.
<P>It's pretty straight-forward. If you're confused, ask.
<P><B>Command Reference</B>
<BR>Arguments in &lt;angle brackets&gt; are required. Arguments in [brackets] are optional. If there is more than one optional argument, you can't skip one and provide one after it. Once you skip one, you have to skip the rest.
<?_code
use strict;
use vars qw(%cmd);
my ($ret, $sth);
$ret .= "<dl>";
foreach my $cmdname (sort keys %LJ::Con::cmd)
{
my $cmd = $LJ::Con::cmd{$cmdname};
next if ($cmd->{'hidden'});
my $args = LJ::ehtml($cmd->{'argsummary'});
my $anchor = "cmd.$cmdname";
$ret .= "<a name='$anchor'><dt><p><table width=100% cellpadding=2><tr><td bgcolor=#d0d0d0>";
$ret .= "<tt><a style='text-decoration: none' href='\#$anchor'><b>$cmdname</b></a> $args</tt></td></tr></table>";
$ret .= "</dt><dd><p>$cmd->{'des'}";
if ($cmd->{'args'}) {
my @des = @{$cmd->{'args'}};
$ret .= "<p><dl>";
while (my ($arg, $des) = splice(@des, 0, 2)) {
$ret .= "<dt><b><i>$arg</i></b></dt><dd>$des</dd>";
}
$ret .= "</dl>";
}
$ret .= "</dd></a>";
}
$ret .= "</dl>";
return $ret;
_code?>
</body></html><?_c <LJDEP>
lib: cgi-bin/console.pl, cgi-bin/ljlib.pl
link: htdocs/admin/console/index.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,200 @@
<html>
<head><title>DB Admin - <?sitename?></title>
<style>
p, td { font-size: 9pt; font-family: sans-serif; }
input { font-size: 8pt; font-family: sans-serif; }
</style>
</head>
<body bgcolor='#000000' text='#ffffff' link='#eeeeee' vlink='#eeeeee'>
<?_code
use strict;
use vars qw(%FORM);
my $dbh = LJ::get_db_writer();
my $remote = LJ::get_remote($dbh);
return"<b>Error:</b> You don't have access to administer databases."
unless (LJ::check_priv($dbh, $remote, "siteadmin", "dbweightview"));
my $can_save = LJ::check_priv($dbh, $remote, "siteadmin", "dbweightchange");
my $view = $FORM{'view'} eq "role" ? "role" : "host";
my %dbinfo; # dbid
my %slaves; # dbid
my $sth;
$sth = $dbh->prepare("SELECT dbid, name, fdsn, masterid FROM dbinfo");
$sth->execute;
while ($_ = $sth->fetchrow_hashref) {
next unless $_->{'dbid'};
$dbinfo{$_->{'dbid'}} = $_;
push @{$slaves{$_->{'masterid'}}}, $_->{'dbid'};
}
my %weights;
my %role;
my %roletweight;
$sth = $dbh->prepare("SELECT dbid, role, norm, curr FROM dbweights");
$sth->execute;
while ($_ = $sth->fetchrow_hashref) {
next unless defined $dbinfo{$_->{'dbid'}};
$weights{$_->{'dbid'}}->{$_->{'role'}} = $_;
push @{$role{$_->{'role'}}}, $_->{'dbid'};
$roletweight{$_->{'role'}} += $_->{'curr'};
}
my $ret;
my $p = sub { $ret .= shift; };
my $status;
if ($can_save && defined $FORM{'action:save'}) {
return "<b>Error:</b> Not a POST request." unless LJ::did_post();
my $curr_changed = 0;
foreach my $k (keys %FORM) {
next unless $k =~ /^set-(\d+)-(\w+?)-(\w+)$/;
my ($sid, $role, $what) = ($1, $2, $3);
next unless $what eq "norm" or $what eq "curr";
next unless defined $weights{$sid};
next unless defined $weights{$sid}->{$role};
my $val = $FORM{$k}+0;
my $old = $weights{$sid}->{$role}->{$what};
next if $val == $old;
$dbh->do("UPDATE dbweights SET $what=$val WHERE ".
"dbid=$sid AND role='$role'");
$weights{$sid}->{$role}->{$what} = $val;
if ($what eq "curr") {
$curr_changed = 1;
$roletweight{$role} += $val - $old;
}
}
if ($curr_changed) {
if ($LJ::DBCONFIG_WRITE_CONFIG) {
my $good = 1;
my $newcfg = "# auto-generated config from /admin/dbadmin.bml. changes will be overwritten!\n";
$newcfg .= "%LJ::DBINFO = (\n";
foreach my $id (sort keys %dbinfo) {
next unless $id > 0;
my $dbinf = $dbinfo{$id};
my $name = $dbinf->{'name'};
if ($dbinf->{'masterid'} == 0) { $name = "master"; }
$newcfg .= "\t'$name' => {\n";
$newcfg .= "\t\t'_fdsn' => \"$dbinf->{fdsn}\",\n";
$newcfg .= "\t\t'role' => {\n";
foreach my $role (sort keys %{$weights{$id} || {}}) {
$newcfg .= "\t\t\t'$role' => $weights{$id}->{$role}->{'curr'},\n";
}
$newcfg .= "\t\t},\n";
$newcfg .= "\t},\n";
}
$newcfg .= ");\n";
$newcfg .= "\$LJ::DBINFO{'master'}->{'role'}->{'master'} = 1;\n";
$newcfg .= "1;\n";
if (open(CFG, ">$ENV{'LJHOME'}/cgi-bin/dbconfig.pl")) {
print CFG $newcfg;
close CFG;
open (TCH, ">>$ENV{'LJHOME'}/cgi-bin/ljconfig.pl");
close TCH;
}
$status .= "<p>Wrote config.</p>";
} else {
my $newserial = LJ::procnotify_add("DBI::Role::reload");
$status .= "<p>New Serial: $newserial</p>\n";
}
}
}
my $single = sub {
my $role = shift;
return @{$role{$role}} == 1;
};
my $slaveroleperc = sub {
my $sid = shift;
my $role = shift;
return sprintf("%0.1f%%", 100*$weights{$sid}->{$role}->{'curr'}/($roletweight{$role}||1));
};
my $dumpslaves = sub
{
my $mid = shift;
my $depth = shift;
my $rec = shift;
return unless $slaves{$mid};
my $indent = "&nbsp;" x ($depth*5);
foreach my $sid (sort { $#{$slaves{$a}} <=> $#{$slaves{$b}} } @{$slaves{$mid}}) {
my $db = $dbinfo{$sid};
$p->("<tr bgcolor='#404070'><td colspan='4'><b>$indent$db->{'name'}</b> ($sid)</td></tr>");
foreach my $role (sort keys %{$weights{$sid}}) {
my $r = $weights{$sid}->{$role};
my $col;
if ($r->{'norm'} != $r->{'curr'}) {
$col = "bgcolor='#800000'";
}
$p->("<tr valign='bottom' $col><td>$indent$role</td>");
$p->("<td align='center'><input size='3' name='set-$sid-$role-norm' value='$r->{'norm'}'></td>");
$p->("<td align='center'><input size='3' name='set-$sid-$role-curr' value='$r->{'curr'}'></td>");
$p->("<td>" . $slaveroleperc->($sid, $role) . "</td>");
$p->("</tr>");
}
$rec->($sid, $depth+1, $rec);
}
};
my $dumprole = sub
{
my $role = shift;
return if $single->($role);
$p->("<tr bgcolor='#404070'><td colspan='4'><b>$role</b></td></tr>");
foreach my $sid (sort { $weights{$b}->{$role}->{'curr'} <=> $weights{$a}->{$role}->{'curr'} } @{$role{$role}})
{
my $r = $weights{$sid}->{$role};
my $col;
$col = "bgcolor='#800000'" if $r->{'norm'} != $r->{'curr'};
$p->("<tr valign='bottom' $col><td>$dbinfo{$sid}->{'name'}</td>");
$p->("<td align='center'><input size='3' name='set-$sid-$role-norm' value='$r->{'norm'}'></td>");
$p->("<td align='center'><input size='3' name='set-$sid-$role-curr' value='$r->{'curr'}'></td>");
$p->("<td>" . $slaveroleperc->($sid, $role) . "</td>");
$p->("</tr>");
}
};
$p->('<form method="post" action="dbadmin.bml">');
$p->("<input type='hidden' name='view' value='$view'>");
$p->('<table cellpadding="1" border="0" bgcolor="#606060">');
my $hr = "<b>Host</b> / <a href='dbadmin.bml?view=role'>Role</a>";
if ($view eq "role") {
$hr = "<a href='dbadmin.bml?view=host'>Host</a> / <b>Role</b>";
}
$p->("<tr bgcolor='#404040'><td>$hr</td><td><b>Norm</b></td><td><b>Curr</b></td><td>%</td></tr>");
if ($view eq "role") {
foreach my $role (sort keys %role) {
$dumprole->($role);
}
} else {
$dumpslaves->(0, 0, $dumpslaves); # root
}
if ($can_save) {
$p->('<tr><td colspan="4" align="center" bgcolor="#404040"><input type="submit" name="action:refresh" value="Refresh"> ');
$p->('<input type="submit" name="action:save" value="Save"></td></tr>');
}
$p->('</table>');
$p->('</form>');
$p->($status);
return $ret;
_code?>
</body>
</html>

View File

@@ -0,0 +1,100 @@
<?_info
nocache=>1
_info?><?page
title=><?_code return $FORM{'id'} ? "Edit FAQ Item #$FORM{'id'}" : "Add to FAQ"; _code?>
body<=
<CENTER>
<A HREF="./"><B>(Back to FAQ Index)</B></A>
</CENTER>
<FORM ACTION="faqedit_do.bml" METHOD=POST>
<?_code
$id = $FORM{'id'} + 0;
$ret = "";
my $dbh = LJ::get_db_writer();
my $remote = LJ::get_remote();
my %ac_edit;
my %ac_add;
LJ::remote_has_priv($remote, "faqadd", \%ac_add);
if ($id)
{
LJ::remote_has_priv($remote, "faqedit", \%ac_edit);
my $sth = $dbh->prepare("SELECT question, answer, faqcat, sortorder FROM faq WHERE faqid=$id");
$sth->execute;
($question, $answer, $faqcat, $sortorder) = $sth->fetchrow_array or
return "<b>Error:</b> FAQ #$id does not exist.";
$q = LJ::ehtml($question);
$a = LJ::ehtml($answer);
unless ($ac_edit{'*'} || $ac_edit{$faqcat}) {
if (%ac_edit) {
return "<B>Error: </B> You do not have access to edit a FAQ question in the \"$faqcat\" category.";
} else {
return "<B>Error: </B> You do not have access to edit the FAQ.";
}
}
}
else
{
unless (%ac_add) {
return "<B>Error: </B> You do not have access to add to the FAQ.";
}
}
$sortorder += 0;
$sortorder ||= 50;
$ret .= "<INPUT TYPE=HIDDEN NAME=id VALUE=$id>\n";
$ret .= "<P>Category: <SELECT NAME=\"faqcat\"><OPTION VALUE=\"\">\n";
my $sth = $dbh->prepare("SELECT faqcat, faqcatname FROM faqcat ORDER BY catorder");
$sth->execute;
while (($fc, $fcname) = $sth->fetchrow_array)
{
if ($id) {
next unless ($ac_add{'*'} || $ac_add{$fc} || ($fc eq $faqcat));
} else {
next unless ($ac_add{'*'} || $ac_add{$fc});
}
$selected = ($fc eq $faqcat) ? " SELECTED" : "";
$ret .= "<OPTION VALUE=\"$fc\"$selected>" . LJ::ehtml($fcname) . "\n";
}
$ret .= "</SELECT>";
$ret .= "SortOrder (1-100): <INPUT NAME=sortorder SIZE=5 MAXLENGTH=4 VALUE=$sortorder>";
$ret .= "<BR><FONT SIZE=-1>(sort order is how to sort within the category. categories themselves are also sorted.)</FONT>";
$ret .= "<P><B>Question:</B> (as brief as possible, do not span multiple lines)<BR><TEXTAREA NAME=\"q\" ROWS=3 COLS=70 WRAP=SOFT>$q</TEXTAREA><BR><FONT SIZE=-1>(erase question to delete FAQ entry)</FONT>\n";
$ret .= "<P><B>Answer:</B> (long as you want, give URLs to links, not HTML)<BR><TEXTAREA NAME=\"a\" ROWS=15 COLS=70 WRAP=SOFT>$a</TEXTAREA>\n";
my $faqd = LJ::Lang::get_dom("faq");
if ($faqd) {
$ret .= "<p><b>Select modification level:</b> ";
$ret .= LJ::html_select({ 'name' => "sev", "selected" => 1 },
0 => "Typo/etc (no notify)",
1 => "Minor (notify translators)",
2 => "Major (require translation updates)");
$ret .= "</p>";
}
$ret .= "<P><INPUT TYPE=SUBMIT VALUE=\"Add/Edit FAQ Item\">";
return $ret;
_code?>
</FORM>
<=body
page?><?_c <LJDEP>
lib: cgi-bin/ljlib.pl
post: htdocs/admin/faq/faqedit_do.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,109 @@
<?_info
nocache=>1
_info?><?page
title=><?_code return $FORM{'id'} ? "Edit FAQ Item #$FORM{'id'}" : "Add to FAQ"; _code?>
body<=
<CENTER>
<A HREF="./"><B>(Back to FAQ Index)</B></A>
</CENTER>
<P>
<?_code
my $dbh = LJ::get_db_writer();
my $remote = LJ::get_remote();
my %ac_edit;
my %ac_add;
$id = $FORM{'id'} + 0;
$ret = "";
$qq = $dbh->quote($FORM{'q'});
$qa = $dbh->quote($FORM{'a'});
$qfaqcat = $dbh->quote($FORM{'faqcat'});
$sortorder = $FORM{'sortorder'}+0 || 50;
return "<?requirepost?>" unless LJ::did_post();
if ($id)
{
LJ::remote_has_priv($remote, "faqedit", \%ac_edit);
my $sth = $dbh->prepare("SELECT faqcat FROM faq WHERE faqid=$id");
$sth->execute;
my ($faqcat) = $sth->fetchrow_array;
unless ($ac_edit{'*'} || $ac_edit{$faqcat}) {
if (%ac_edit) {
return "<B>Error: </B> You do not have access to edit a FAQ question in the \"$faqcat\" category.";
} else {
return "<B>Error: </B> You do not have access to edit the FAQ.";
}
}
}
else
{
LJ::remote_has_priv($remote, "faqadd", \%ac_add);
unless ($ac_add{'*'} || $ac_add{$FORM{'faqcat'}}) {
return "<B>Error: </B> You do not have access to add FAQ questions in this category";
}
}
my $faqd = LJ::Lang::get_dom("faq");
my $rlang = LJ::Lang::get_root_lang($faqd);
unless ($rlang) { undef $faqd; }
my $opts = {
'changeseverity' => $FORM{'sev'}+0,
};
my $do_trans = sub {
my $id = shift;
return unless $faqd;
LJ::Lang::set_text($dbh, $faqd->{'dmid'}, $rlang->{'lncode'},
"$id.1question", $FORM{'q'}, $opts);
LJ::Lang::set_text($dbh, $faqd->{'dmid'}, $rlang->{'lncode'},
"$id.2answer", $FORM{'a'}, $opts);
};
unless ($id)
{
unless ($FORM{'faqcat'})
{
return "<B>Error: </B> You did not select a FAQ category.";
}
$dbh->do("INSERT INTO faq (faqid, question, answer, faqcat, sortorder, lastmoduserid, lastmodtime) VALUES (NULL, $qq, $qa, $qfaqcat, $sortorder, $remote->{'userid'}, NOW())");
$id = $dbh->{'mysql_insertid'};
$ret .= $dbh->errstr || "Added FAQ item. All good.";
$opts->{'childrenlatest'} = 1;
$do_trans->($id) if $id;
}
else
{
if ($FORM{'q'} =~ /\S/)
{
$dbh->do("UPDATE faq SET question=$qq, answer=$qa, faqcat=$qfaqcat, lastmoduserid=$remote->{'userid'}, lastmodtime=NOW(), sortorder=$sortorder WHERE faqid=$id");
$ret .= "Updated FAQ item. All good. faqid is <b><a href='$LJ::SITEROOT/support/faqbrowse.bml?faqid=$id'>$id</a></b>";
$do_trans->($id);
}
else
{
$dbh->do("DELETE FROM faq WHERE faqid=$id");
$ret .= "FAQ item deleted.";
# TODO: delete translation from ml_* ?
}
}
return $ret;
_code?>
<=body
page?><?_c <LJDEP>
lib: cgi-bin/ljlib.pl
link: htdocs/admin/faq/index.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,67 @@
<?_info
nocache=>1
_info?><?page
title=>FAQ
body<=
<?_code
my $dbh = LJ::get_db_writer();
my $remote = LJ::get_remote();
my %ac_add;
my %ac_edit;
LJ::remote_has_priv($remote, "faqadd", \%ac_add);
LJ::remote_has_priv($remote, "faqedit", \%ac_edit);
$ret = "";
if (%ac_add)
{
$ret .= "<A HREF=\"faqedit.bml\">[Add to FAQ]</A>\n";
}
my %faqcat;
my %faqq;
$sth = $dbh->prepare("SELECT faqcat, faqcatname, catorder FROM faqcat");
$sth->execute;
while ($_ = $sth->fetchrow_hashref)
{
$faqcat{$_->{'faqcat'}} = $_;
}
$sth = $dbh->prepare("SELECT faqid, question, sortorder, faqcat, lastmodtime FROM faq");
$sth->execute;
while ($_ = $sth->fetchrow_hashref)
{
$faqq{$_->{'faqcat'}}->{$_->{'faqid'}} = $_;
}
foreach my $faqcat (sort { $faqcat{$a}->{'catorder'} <=> $faqcat{$b}->{'catorder'} } keys %faqcat)
{
$ret .= "<H2><A HREF=\"readcat.bml?faqcat=$faqcat\">" . LJ::ehtml($faqcat{$faqcat}->{'faqcatname'}) . "</A></H2>\n";
$ret .= "<UL>\n";
foreach my $faqid (sort { $faqq{$faqcat}->{$a}->{'sortorder'} <=> $faqq{$faqcat}->{$b}->{'sortorder'} } keys %{$faqq{$faqcat}})
{
my $fe = $faqq{$faqcat}->{$faqid};
next unless ($fe->{'question'});
my $q = LJ::ehtml($fe->{'question'});
$q =~ s/^\s+//; $q =~ s/\s+$//;
$q =~ s/\n/<BR>/g;
$ret .= "<LI>";
if ($ac_edit{'*'} || $ac_edit{$faqcat}) {
$ret .= "<A HREF=\"faqedit.bml?id=$faqid\">[edit]</A> ($fe->{'sortorder'}) ";
}
$ret .= "<B>{$faqid}</B> $q\n";
}
$ret .= "</UL>\n";
}
return $ret;
_code?>
<=body
page?><?_c <LJDEP>
lib: cgi-bin/ljlib.pl
link: htdocs/admin/faq/readcat.bml, htdocs/admin/faq/faqedit.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,63 @@
<?_info
nocache=>1
_info?><?page
title=>Read FAQ
body<=
<CENTER>
<A HREF="./"><B>(Back to FAQ Index)</B></A>
</CENTER>
<?_code
my $dbh = LJ::get_db_writer();
$ret = "";
my $qfaqcat = $dbh->quote($FORM{'faqcat'});
my %faqcat;
my %faqq;
$sth = $dbh->prepare("SELECT faqcat, faqcatname, catorder FROM faqcat WHERE faqcat=$qfaqcat");
$sth->execute;
while ($_ = $sth->fetchrow_hashref)
{
$faqcat{$_->{'faqcat'}} = $_;
}
$sth = $dbh->prepare("SELECT faqid, question, sortorder, faqcat, answer, lastmodtime FROM faq WHERE faqcat=$qfaqcat");
$sth->execute;
while ($_ = $sth->fetchrow_hashref)
{
$faqq{$_->{'faqid'}} = $_;
}
foreach my $faqcat (sort { $faqcat{$a}->{'catorder'} <=> $faqcat{$b}->{'catorder'} } keys %faqcat)
{
$ret .= "<h2>" . LJ::ehtml($faqcat{$faqcat}->{'faqcatname'}) . "</h2>\n";
$ret .= "<ul>\n";
foreach my $faqid (sort { $faqq{$a}->{'sortorder'} <=> $faqq{$b}->{'sortorder'} } grep { $faqq{$_}->{'faqcat'} eq $faqcat } keys %faqq)
{
next unless ($faqq{$faqid}->{'question'});
BML::note_mod_time($faqq{$faqid}->{'lastmodtime'});
my $q = LJ::ehtml($faqq{$faqid}->{'question'});
$q =~ s/^\s+//; $q =~ s/\s+$//;
$q =~ s!\n!<br />!g;
my $a = LJ::ehtml($faqq{$faqid}->{'answer'});
$a =~ s/^\s+//; $a =~ s/\s+$//;
$a =~ s/\n( +)/"\n" . "&nbsp;&nbsp;"x length($1)/eg;
$a =~ s!\n!<br />!g;
$ret .= "<p><table bgcolor='#c0c0c0'><tr><td><b>$q</b></td></tr></table>" . LJ::auto_linkify($a);
}
$ret .= "</ul>\n";
}
return $ret;
_code?>
<=body
page?><?_c <LJDEP>
lib: cgi-bin/ljlib.pl
link: htdocs/admin/faq/index.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,150 @@
<?_code
my ($ret, $sth);
my $DEF_ROW = 30;
my $DEF_COL = 80;
my $remote = LJ::get_remote();
my %files = (); # keys: files remote user has access to, value: 1
my $INC_DIR = $LJ::BML_INC_DIR_ADMIN || $LJ::BML_INC_DIR || "$LJ::HTDOCS/inc";
unless (LJ::remote_has_priv($remote, "fileedit", \%files)) {
return "You don't have access to edit any files, or you're not logged in.";
}
my $valid_filename = sub
{
my $filename = shift;
return ($filename =~ /^[a-zA-Z0-9-\_]{1,80}$/);
};
my $save_file = sub
{
my ($filename, $content) = @_;
return 0 unless $valid_filename->($filename);
if ($LJ::FILEEDIT_VIA_DB || $LJ::FILEEDIT_VIA_DB{$filename}) {
my $dbh = LJ::get_db_writer();
$dbh->do("REPLACE INTO includetext (incname, inctext, updatetime) ".
"VALUES (?, ?, UNIX_TIMESTAMP())", undef, $filename, $content);
return 0 if $dbh->err;
LJ::MemCache::set("includefile:$filename", $content);
return 1;
}
open (FILE, ">$INC_DIR/$filename") or return 0;
print FILE $content;
close FILE;
return 1;
};
my $load_file = sub
{
my ($filename) = @_;
return undef unless $valid_filename->($filename);
my $contents;
if ($LJ::FILEEDIT_VIA_DB || $LJ::FILEEDIT_VIA_DB{$filename}) {
my $dbh = LJ::get_db_writer();
$contents = $dbh->selectrow_array("SELECT inctext FROM includetext WHERE incname=?", undef, $filename);
return $contents if defined $contents;
}
open (FILE, "$INC_DIR/$filename") or return undef;
while (<FILE>) { $contents .= $_; }
close FILE;
return $contents;
};
if ($files{'*'})
{
# if user has access to edit all files, find what those files are!
delete $files{'*'};
opendir (DIR, $INC_DIR);
while (my $file = readdir(DIR)) {
$files{$file} = 1;
}
closedir (DIR);
}
## get rid of files that don't match our safe pattern
{
my @del;
foreach my $k (keys %files) {
push @del, $k
unless $valid_filename->($k);
}
foreach my $k (@del) { delete $files{$k}; }
}
my $mode = $FORM{'mode'};
unless ($mode) {
$mode = $FORM{'file'} ? "edit" : "pick";
}
if ($mode eq "pick")
{
$ret .= "<FORM METHOD=GET>\n";
$ret .= "Pick file to edit: <SELECT NAME=\"file\">";
foreach my $file (sort keys %files) {
$ret .= "<OPTION VALUE=\"$file\">$file\n";
}
$ret .= "</SELECT> <INPUT TYPE=SUBMIT VALUE=\"load...\"><BR>";
$ret .= "Wordwrap? <INPUT TYPE=CHECKBOX VALUE=1 NAME=w> ";
$ret .= "Rows: <INPUT SIZE=3 NAME=r VALUE=$DEF_ROW> ";
$ret .= "Cols: <INPUT SIZE=3 NAME=c VALUE=$DEF_COL> ";
$ret .= "</FORM>";
return $ret;
}
my $file = $FORM{'file'};
unless ($files{$file}) {
return "<B>ERROR!</B> you don't have access to this document.";
}
if ($mode eq "edit")
{
$ret .= "<B>Editing:</B> <tt>$file</tt><P>";
my $contents = $load_file->($file);
return "<B>Error:</B> Couldn't open file"
unless defined $contents;
my $r = ($FORM{'r'}+0) || $DEF_ROW;
my $c = ($FORM{'c'}+0) || $DEF_COL;
my $wrap = $FORM{'w'} ? "SOFT" : "OFF";
$ret .= "<FORM METHOD=POST>\n";
$ret .= "<INPUT TYPE=HIDDEN NAME=mode VALUE=\"save\">";
$ret .= "<INPUT TYPE=HIDDEN NAME=file VALUE=\"$file\">";
$ret .= "<TEXTAREA ROWS=$r COLS=$c WRAP=$wrap NAME=contents>";
$ret .= BML::eall($contents);
$ret .= "</TEXTAREA><P><INPUT TYPE=SUBMIT VALUE=\"Save\"> (no undo.. are you sure?)";
$ret .= "</FORM>\n";
return $ret;
}
if ($mode eq "save")
{
unless (LJ::did_post()) {
return "<b>Error:</b> requires post";
}
$ret .= "<B>Saving:</B> <tt>$file</tt><p>";
if ($save_file->($file, $FORM{'contents'})) {
$ret .= "saved.";
} else {
$ret .= "<b>Error saving</b>";
}
return $ret;
}
return "unknown mode";
_code?><?_c <LJDEP>
lib: cgi-bin/ljlib.pl
form: htdocs/admin/fileedit/index.bml
post: htdocs/admin/fileedit/index.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,170 @@
<?_code
{
#line 3
use strict;
no strict 'refs';
use vars qw(%GET);
use Data::Dumper;
use Time::HiRes ();
my $u = LJ::get_remote();
return "You must be logged in to view this tool." unless $u;
return "You don't have 'siteadmin' priv." unless LJ::check_priv($u, "siteadmin");
my $prev_hits = $u ? LJ::MemCache::get([$u->{'userid'},"mcrate:$u->{'userid'}"]) : undef;
my $ret;
my $mode = $GET{'mode'};
if ($GET{'host'}) {
$mode ||= "host";
}
$mode ||= "overview";
$ret .= "<div class='topbar'>[<a href='memcache.bml'>Overview</a>]\n";
if ($mode eq "overview") {
$ret .= <<"END_TOP";
</div>
<h1>Memory Cache Overview</h1>
<table border='1' cellpadding='5'>
<tr><th>Host</th><th>Hit Rate</th><th>Curr/Max Size</th><th><span title='Utilization'>Utlz %</span></th><th>Uptime</th><th>Version</th></tr>
END_TOP
}
my %now_hits;
if ($prev_hits) { %now_hits = %$prev_hits; }
my ($tot_hits, $tot_misses) = ();
foreach my $entry (@LJ::MEMCACHE_SERVERS) {
my $host = ref $entry ? $entry->[0] : $entry;
next if $mode eq "host" && $host ne $GET{'host'};
LJ::MemCache::forget_dead_hosts();
my $sock = Cache::Memcached::sock_to_host($host);
my $t1 = Time::HiRes::time();
my $log;
my %stat;
my @cmds = ("", "malloc", "items", "slabs");
my $cmd;
if ($sock) {
while (defined($cmd = shift @cmds)) {
my $realcmd = "stats" . ($cmd ? " $cmd" : "");
$log .= "<b>$realcmd</b>\n";
foreach (LJ::MemCache::run_command($sock, "$realcmd\r\n")) {
last if $_ eq "END\r\n";
$log .= $_;
next if $cmd eq "maps";
if (/^STAT (\S+) (\S+)/) {
$stat{$cmd}{$1} = $2;
}
}
}
}
my $t2 = Time::HiRes::time();
my $cpu = 0;
foreach my $key (qw(rusage_user rusage_system)) {
my $sec = $stat{''}{$key};
$sec =~ s/:/\./;
$cpu += $sec;
#$ret .= "Host $host was $stat{''}{$key} = $sec, cpu = $cpu<br />\n";
}
$now_hits{$host} = [ $stat{''}{'get_hits'}, $stat{''}{'get_misses'}, $cpu ];
my $hit_rate = sprintf("%0.02f%%", $stat{''}{'get_hits'}/($stat{''}{'get_hits'}+$stat{''}{'get_misses'}||1)*100);
if ($mode eq "overview") {
$ret .= "<tr><td><a href='memcache.bml?host=$host'>$host</a></td>\n";
$ret .= "<td>$hit_rate";
if ($prev_hits && $prev_hits->{$host}) {
my $nh = $now_hits{$host};
my $ph = $prev_hits->{$host};
my $new_hits = $now_hits{$host}[0] - $prev_hits->{$host}[0];
my $new_misses = $now_hits{$host}[1] - $prev_hits->{$host}[1];
$tot_hits += $new_hits;
$tot_misses += $new_misses;
my $new_whatev = $new_hits + $new_misses;
my $new_rate = $new_hits / ($new_whatev || 1);
my $cpu = sprintf("%0.6f", $nh->[2] - $ph->[2]);
$ret .= sprintf(" [%0.02f%% {$new_whatev} $cpu]", $new_rate * 100);
}
$ret .= sprintf(" %0.02f", $t2-$t1);
$ret .= "</td>";
my $gb_used = ($stat{'malloc'}{'mmapped_space'} + $stat{'malloc'}{'arena_size'}) / (1024*1024*1024);
my $gb_max = $stat{''}{'limit_maxbytes'} / (1024*1024*1024);
if ($gb_used >= $gb_max) {
$ret .= sprintf("<td align='center'>%0.01fG</td>", $gb_max);
} else {
$ret .= sprintf("<td>%0.02f/%0.01fG (%0.02f%%)</td>", $gb_used, $gb_max, $gb_used*100/($gb_max||1));
}
my $utiliz = $stat{''}{'bytes'} /
(($stat{'malloc'}{'mmapped_space'} + $stat{'malloc'}{'arena_size'}) || 1);
$ret .= sprintf("<td>%0.02f%%</td>", $utiliz*100);
my $up = $stat{''}{'uptime'};
my $upstring;
foreach my $u ([86400,"d"],[3600,"h"],[60,"m"],[1,"s"]) {
if ($up / $u->[0] > 1) {
my $v = int($up / $u->[0]);
$upstring .= "${v}$u->[1] ";
$up -= $v * $u->[0];
}
}
$ret .= "<td>$upstring</td>";
$ret .= "<td>$stat{''}{'version'}</td>";
$ret .= "</tr>";
}
if ($mode eq "host" && $host eq $GET{'host'}) {
$ret .= "[<a href='memcache.bml?host=$host&amp;mode=raw'>Raw Data</a>]</div>";
$ret .= "<h1>Details for $host</h1>";
$ret .= "<h2>Slab classes</h2>";
$ret .= "<table border='1' cellpadding='2'>";
$ret .= "<tr><th>class</th><th>size</th><th>used</th><th>total</th><th colspan='2'>free</th><th>pages</th><th>max age</th></tr>\n";
foreach my $cls (0..31) {
my $size = $stat{'slabs'}{"$cls:chunk_size"};
next unless $size;
$ret .= "<tr><td>$cls</td>"
. join('', map { "<td>" . $stat{'slabs'}{"$cls:$_"} . "</td>" }
qw(chunk_size used_chunks total_chunks free_chunks free_chunks_end total_pages));
my $age = $stat{'items'}{"items:$cls:age"};
$ret .= "<td>$age</td>";
$ret .= "</tr>";
}
$ret .= "</table>\n";
}
if ($mode eq "raw" && $host eq $GET{'host'}) {
$ret .= "[<a href='memcache.bml?host=$host'>Host Stats</a>]</div>";
$ret .= "<h1>Raw data for $host</h1>";
$ret .= "<pre>$log</pre>";
}
}
LJ::MemCache::set([$u->{'userid'},"mcrate:$u->{'userid'}"], \%now_hits)
if $u;
if ($mode eq "overview") {
$ret .= "</table>\n";
my $new_whatev = $tot_hits + $tot_misses;
my $new_rate = $tot_hits / ($new_whatev || 1);
$ret .= sprintf("Global [%0.02f%% {$new_whatev}]", $new_rate * 100);
}
return $ret;
}
_code?>

View File

@@ -0,0 +1,275 @@
<html>
<head><title>Memcache view</title>
<body>
<?_code
use strict;
use vars qw(%GET %POST);
use Data::Dumper;
my $ret;
my $remote = LJ::get_remote();
return "<b>Error:</b> You don't have access to viewing memcache info."
unless (LJ::check_priv($remote, "siteadmin", "memcacheview"));
return "<b>Error:</b> No memcache servers defined."
unless @LJ::MEMCACHE_SERVERS;
my $uid = sub {
my $u = LJ::load_user(shift);
return $u ? $u->{'userid'} : "";
};
my $cuid = sub {
my $u = LJ::load_user(shift);
return $u ? "$u->{'clusterid'}:$u->{'userid'}" : "";
};
# key: unique prefix of a memcache key
# value: number n, means the n-th component of the key when
# split by ':' is the hash key. 0 means no hash key.
# the default, when absent from this hash, is "n=2 if the 2nd component
# is a number".
my %MEMC_HASHKEYS = (
'uidof:' => 0,
'talksubject:' => 3,
'talkbody:' => 3,
'logtext:' => 3,
's1pubstyc:' => 0,
'popsyn:' => 0,
'rate_eperr:' => 0,
'rate:' => 0,
'ml.' => 0,
);
my $get_hashkey = sub {
my $key = shift;
return undef unless $key;
my $hk;
my $component;
foreach (keys %MEMC_HASHKEYS) {
if ($key =~ /^$_/) {
$component = $MEMC_HASHKEYS{$_};
}
}
return undef if defined ($component) and $component == 0;
my $sep = ':';
$sep = '.' if $key =~ /userpic\./; #special case
my @els = split (/\Q$sep\E/, $key);
$hk = $els[defined($component) ? $component-1 : 2-1];
$hk = undef
unless defined($component) || int($hk)==$hk;
return $hk;
};
my $display = sub {
my ($key, $val) = @_;
# first, transform array->hash if necessary
$val = LJ::MemCache::array_to_hash("user", $val)
if $key =~ /^user:/
or $key =~ /^userid:/;
# blot out passwords
if (ref $val eq 'HASH' && defined($val->{'password'})) {
$val->{'password'} = '*' x 8;
}
# unpack packed data
if ($key =~ /^talk2:/) {
my $newval;
my $n = (length($val) - 1) / 16;
for (my $i=0; $i<$n; $i++) {
my ($f1, $par, $poster, $time) = unpack("NNNN",substr($val,$i*16+1,16));
my $state = chr($f1 & 255);
my $talkid = $f1 >> 8;
$newval->{$talkid} = {
talkid => $talkid,
state => $state,
posterid => $poster,
datepost => LJ::mysql_time($time),
parenttalkid => $par,
};
}
$val = [substr($val,0,1), $newval];
}
if ($key =~ /^log2:/) {
my $item = {};
@$item{'posterid', 'eventtime', 'logtime', 'allowmask', 'ditemid'} = unpack("NNNNN", $val);
$item->{'security'} = ($item->{'allowmask'} == 0 ? 'private' :
($item->{'allowmask'} == 2**31 ? 'public' : 'usemask'));
@$item{'jitemid', 'anum'} = ($item->{'ditemid'} >> 8, $item->{'ditemid'} % 256);
$item->{'eventtime'} = LJ::mysql_time($item->{'eventtime'}, 1);
$item->{'logtime'} = LJ::mysql_time($item->{'logtime'}, 1);
$val = $item;
}
if ($key =~ /^log2lt:/) {
my $items = [];
my $ver = substr($val, 0, 1);
my $offset = {1=>1, 2=>5, 3=>5}->{$ver};
my $newval;
push @$newval, $ver;
push @$newval, unpack("N", substr($val, 1, 4))
if $ver>=2;
my $n = (length($val) - $offset )/20;
for (my $i=0; $i<$n; $i++) {
my ($posterid, $eventtime, $rlogtime, $allowmask, $ditemid) =
unpack("NNNNN", substr($val, $i*20+$offset, 20));
$eventtime = LJ::mysql_time($eventtime, 1);
my $security = $allowmask == 0 ? 'private' :
($allowmask == 2**31 ? 'public' : 'usemask');
my ($jitemid, $anum) = ($ditemid >> 8, $ditemid % 256);
my $item = {};
@$item{'posterid','eventtime','rlogtime','allowmask','ditemid',
'security', 'jitemid', 'anum'} =
($posterid, $eventtime, $rlogtime, $allowmask,
$ditemid, $security, $jitemid, $anum);
push @$items, $item;
}
push @$newval, $items;
$val = $newval;
}
if ($key =~ /^fgrp:/) {
my $newval = [];
my $ver = shift @$val;
push @$newval, $ver;
foreach(@$val) {
push @$newval, LJ::MemCache::array_to_hash("fgrp", [$ver, @$_]);
}
$val = $newval;
}
if ($key =~ /^upicinf:(\d+)$/) {
my $userid = $1;
my ($ver, $picstr, $kwstr) = @$val;
my $info = {
'version' => $ver,
'pic' => {},
'kw' => {},
};
while (length $picstr >= 7) {
my $pic = { userid => $userid };
($pic->{picid},
$pic->{width}, $pic->{height},
$pic->{state}) = unpack "NCCA", substr($picstr, 0, 7, '');
$info->{pic}{$pic->{picid}} = $pic;
}
my ($pos, $nulpos);
$pos = $nulpos = 0;
while (($nulpos = index($kwstr, "\0", $pos)) > 0) {
my $kw = substr($kwstr, $pos, $nulpos-$pos);
my $id = unpack("N", substr($kwstr, $nulpos+1, 4));
$pos = $nulpos + 5; # skip NUL + 4 bytes.
$info->{kw}{$kw} = $info->{pic}{$id} if $info;
}
$val = $info;
}
if ($key =~ /^friends:/) {
my $ver = substr($val, 0, 1, '');
my $packfmt = "NH6H6NC";
my $packlen = 15;
my @cols = qw(friendid fgcolor bgcolor groupmask showbydefault);
my %friends;
while (length($val) >= $packlen) {
my @row = unpack($packfmt, substr($val, 0, $packlen, ''));
# add "#" to beginning of colors
$row[$_] = "\#$row[$_]" foreach 1..2;
# turn unpacked row into hashref
my $fid = $row[0];
my $idx = 1;
foreach my $col (@cols[1..$#cols]) {
$friends{$fid}->{$col} = $row[$idx];
$idx++;
}
}
$val = [$ver, \%friends];
}
if ($key =~ /^tu:/) {
$val = unpack("N", $val);
}
# just in case this remains a packed scalar
if (not ref $val) {
$val =~ s/([\x00-\x1f])/sprintf("\\x%02x", $1)/eg;
}
$ret .= "<b>Data: </b>";
my $dumper = Data::Dumper->new([$val],["Value"]);
$dumper->Terse(1);
$dumper->Indent(2);
my $d = $dumper->Dump();
$ret.= "<pre>" . LJ::ehtml($d) . "</pre>";
return;
};
if ($POST{'query'}) {
foreach my $key (split(/\r\n/, $POST{'query'})) {
next unless $key =~ /\S/;
# shortcuts
$key =~ s/(##)(\w+)/$cuid->($2)/eg;
$key =~ s/(#)(\w+)/$uid->($2)/eg;
$key =~ s!\((\d+)\)!int($1/256)!eg;
my $sock = LJ::MemCache::_get_sock($key);
$ret .= "<p><b>Key: </b>$key<br />";
unless ($sock) {
$ret .= "<b>Error: </b>Could not connect to server<br /></p>";
next;
}
if ($POST{'sock'}) {
$ret .= "<b>Socket:</b> $sock<br />";
}
my $hashkey = $get_hashkey->($key);
if ($hashkey) {
$ret .= "<b>Hashkey:</b> $hashkey<br />";
}
my $pars = defined($hashkey) ? [$hashkey, $key] : $key;
my $val = LJ::MemCache::get($pars);
unless (defined $val) {
$ret .= "<b>Data:</b> not found</br ></p>";
next;
}
$display->($key, $val);
$ret .= "</p>";
}
return $ret;
}
$ret .= "<p>Enter your memcache query(-ies) below.</p>";
$ret .= '<p>Here\'s the <a href="http://cvs.livejournal.org/browse.cgi/livejournal/doc/raw/memcache-keys.txt?rev=.&content-type=text/x-cvsweb-markup">reference</a> of key names.</p>';
$ret .= "<p>Shortcuts: <blockquote>#username -> userid<br /> ##username -> cid:userid<br />(number) -> number/256 </blockquote></p>";
$ret .= '<form method="post" action="memcache_view.bml">';
$ret .= "<textarea name=query rows=3 cols=60 wrap=off></textarea> ";
$ret .= "<p>" . LJ::html_check({ 'type' => 'check', 'name' => 'sock', 'id' => 'sock' });
$ret .= "<label for='sock'>Show host/port per key.</label></p>";
$ret .= "<input type='submit' value='Submit'>";
return $ret;
_code?>
</body>
</html>

View File

@@ -0,0 +1,129 @@
<?_code
my $format = $FORM{'format'} || "html";
my $remote = LJ::get_remote();
return"<b>Error:</b> You don't have access to administer databases."
unless (LJ::check_priv($remote, "siteadmin", "mysqlstatus"));
my $dbh = LJ::get_db_writer();
if ($format eq "text") {
BML::set_content_type("text/plain");
}
my ($sth, $ret);
if ($format eq "html") {
$ret .= "<style>\ntd { font-family: arial; font-size: 11px };\n</style>\n";
}
my @modes = qw(status variables tables);
my $mode = $FORM{'mode'} || "status";
foreach my $m (@modes) {
if ($mode eq $m) {
$mode = $m;
$ret .= "<b>[ $mode ]</b> " if ($format eq "html");
} else {
if ($format eq "html") {
$ret .= "<b>[</b> <a href=\"" . BML::self_link({'mode' => $m});
$ret .= "\">$m</a> <b>]</b> ";
}
}
}
if ($format eq "html") {
$ret .= "<p>Or, view <a href=\"mysql_status.bml?mode=$mode&format=text\">as text</a>.<p>";
}
if ($mode eq "status")
{
$sth = $dbh->prepare("SHOW STATUS");
$sth->execute;
my %s;
while (my ($k, $v) = $sth->fetchrow_array) {
$s{$k} = $v;
}
$sth = $dbh->prepare("SHOW STATUS");
$sth->execute;
if ($format eq "html") {
$ret .= "<table cellpadding=2 border=1 cellspacing=1>";
}
while (my ($k, $v) = $sth->fetchrow_array) {
my $delta = $v - $s{$k};
if ($delta == 0) {
$delta = "";
} elsif ($delta > 0) {
$delta = "+$delta";
} else {
$delta = "-$delta";
}
if ($format eq "html") {
$ret .= "<tr><td><b>$k</b></td><td>$v</td><td>$delta</td></tr>\n";
} elsif ($format eq "text") {
$ret .= "$k,$v,$delta\n";
}
}
$ret .= "</table>\n" if ($format eq "html");
return $ret;
}
if ($mode eq "variables")
{
$sth = $dbh->prepare("SHOW VARIABLES");
$sth->execute;
$ret .= "<table cellpadding=2 border=1 cellspacing=1>" if ($format eq "html");
while (my ($k, $v) = $sth->fetchrow_array) {
if ($format eq "html") {
$ret .= "<tr><td><b>$k</b></td><td>$v</td></tr>\n";
} else {
$ret .= "$k,$v\n";
}
}
$ret .= "</table>\n" if ($format eq "html");
return $ret;
}
if ($mode eq "tables")
{
$sth = $dbh->prepare("SHOW TABLE STATUS");
$sth->execute;
if ($format eq "html") {
$ret .= "<table cellpadding=2 border=1 cellspacing=1>";
$ret .= "<tr>";
}
my @cols = @{$sth->{'NAME'}};
foreach (@cols) {
if ($format eq "html") {
$ret .= "<td><b>$_</b></td>";
} else {
$ret .= "$_,";
}
}
if ($format eq "html") {
$ret .= "</tr>\n";
} else {
$ret .= "\n";
}
while (my $t = $sth->fetchrow_hashref) {
$ret .= "<tr>" if ($format eq "html");
foreach my $c (@cols) {
if ($format eq "html") {
$ret .= "<td>$t->{$c}</td>";
} elsif ($format eq "text") {
$ret .= "$t->{$c},";
}
}
if ($format eq "html") {
$ret .= "</tr>";
} elsif ($format eq "text") {
$ret .= "\n";
}
}
$ret .= "</table>\n" if ($format eq "html");
return $ret;
}
_code?>

View File

@@ -0,0 +1,338 @@
<html><head>
<meta name="robots" content="noindex, nofollow, noarchive" />
<meta name="googlebot" content="nosnippet" />
<title>Privilege Management</title>
</head><body>
<?_code
use strict;
use vars qw(%FORM);
my $dbh = LJ::get_db_writer;
my ($sth, $ret);
my $mode = $FORM{'mode'};
my $remote = LJ::get_remote();
LJ::load_user_privs($remote, 'admin') if $remote;
my @privs;
my %priv;
my %pcode2id;
$sth = $dbh->prepare("SELECT prlid, privcode, privname, des, is_public, scope FROM priv_list ORDER BY privcode");
$sth->execute;
while ($_ = $sth->fetchrow_hashref) {
push @privs, $_;
$priv{$_->{'prlid'}} = $_;
$pcode2id{$_->{'privcode'}} = $_->{'prlid'};
}
if (LJ::did_post()) {
return "<p><b>ERROR:</b> Invalid form submission" unless LJ::check_form_auth();
}
unless ($mode)
{
if ($FORM{'user'}) { $mode = "viewuser"; }
elsif ($FORM{'priv'}) { $mode = "viewpriv"; }
}
if ($FORM{'devmode'}) {
return "not in dev mode" unless $LJ::IS_DEV_SERVER;
my $userid = $remote->{userid};
if ($dbh->do("INSERT INTO priv_map (userid, prlid, arg) SELECT ?, prlid, ? FROM priv_list WHERE privcode=?",
undef, $userid, $FORM{arg}, $FORM{priv})) {
LJ::statushistory_add($dbh, $userid, $userid, "privadd", "DEVMODE Granting: \"$FORM{priv}\" with arg \"$FORM{arg}\"");
return "done.";
} else {
return "fail.";
}
}
unless ($mode)
{
$ret .= "<h1>Privilege Management</h1>\n";
$ret .= "<form method='get' action='index.bml'>";
$ret .= "<p>View all privileges of user <input name='user' size='15' /> <input type='submit' value=\"Load\" /></p></form>";
$ret .= "<p>Or, show all users with privilege:</p><dl>";
foreach my $priv (@privs) {
my ($des, $args) = split(/arg=/, $priv->{'des'});
$ret .= "<dt><strong><a href='./?priv=$priv->{'privcode'}'>$priv->{'privcode'}</a>: $priv->{'privname'}</strong>";
$ret .= " <i>(Site Specific)</i>" if $priv->{'scope'} eq 'local';
$ret .= "</dt>";
$ret .= "<dd>$des\n";
$ret .= "<br /><strong>Argument:</strong> $args" if $args;
$ret .= "</dd>";
}
$ret .= "</dl>";
return $ret;
}
# Returns true if the remote user can grant the given priv
sub remote_can_grant
{
my ($remote, $priv, $arg) = @_;
return 0 unless defined $priv;
return LJ::check_priv($remote, 'admin', $priv) || LJ::check_priv($remote, 'admin', '*') || LJ::check_priv($remote, 'admin', "$priv/$arg");
}
if ($mode eq "userchange" || $mode eq "privchange")
{
unless (LJ::did_post()) {
return "<p><b>Error:</b> requires post</p>";
}
unless ($FORM{'submit:refresh'}) {
foreach my $key (keys %FORM) {
if ($key =~ /^revoke:(\d+):(\d+)$/) {
my $prmid = $1;
my $del_userid1 = $2;
my $sth = $dbh->prepare("SELECT userid, prlid, arg FROM priv_map WHERE prmid=$prmid");
$sth->execute;
my ($del_userid2, $prlid, $arg) = $sth->fetchrow_array;
unless (remote_can_grant($remote, $priv{$prlid}->{'privcode'}, $arg)) {
$ret .= "ERROR: Invalid access to remove priv $priv{$prlid}->{'privcode'}.<br />";
} else {
if ($del_userid1 && $del_userid1 == $del_userid2)
{
$dbh->do("DELETE FROM priv_map WHERE prmid=$prmid");
my $privcode = $priv{$prlid}->{'privcode'};
LJ::statushistory_add($dbh, $del_userid1, $remote->{'userid'}, "privdel",
"Denying: \"$privcode\" with arg \"$arg\"");
$ret .= "Privilege removed.<br />\n";
}
}
}
}
if ($FORM{'grantpriv'}) {
my $u = LJ::load_user($FORM{'user'});
return "ERROR: Invalid user." unless $u;
my $userid = $u->{'userid'};
my $qpriv = $FORM{'grantpriv'}+0;
my $privcode = $priv{$qpriv}->{'privcode'};
my $arg = $FORM{'arg'};
if ($privcode) {
if (remote_can_grant($remote, $privcode, $arg)) {
if (LJ::check_priv($u, $privcode, $arg)) {
$ret .= "ERROR: User already has specified priv <b>$privcode $arg</b>.<br />";
} else {
my $qarg = $dbh->quote($arg);
$dbh->do("INSERT INTO priv_map (prmid, userid, prlid, arg) VALUES (NULL, $userid, $qpriv, $qarg)");
LJ::statushistory_add($dbh, $userid, $remote->{'userid'}, "privadd", "Granting: \"$privcode\" with arg \"$arg\"");
$ret .= "Privilege <b>$privcode $arg</b> granted.<br />\n";
}
} else {
$ret .= "ERROR: You don't have access to grant <b>$privcode $arg</b>.<br />\n";
}
} else {
$ret .= "ERROR: Unknown privilege.<br />\n";
}
}
if ($FORM{'grantuser'}) {
my $u = LJ::load_user($FORM{'grantuser'});
return "ERROR: Invalid user." unless $u;
my $userid = $u->{'userid'};
my $privid = $pcode2id{$FORM{'priv'}};
my $arg = $FORM{'arg'};
my $qarg = $dbh->quote($arg);
my $privcode = $priv{$privid}->{'privcode'};
if ($privcode) {
if (remote_can_grant($remote, $privcode, $arg)) {
if (LJ::check_priv($u, $privcode, $arg)) {
$ret .= "ERROR: User already has specified priv <b>$privcode $arg</b>.<br />";
}
elsif ($userid && $privid) {
my $qarg = $dbh->quote($FORM{'arg'});
$dbh->do("INSERT INTO priv_map (prmid, userid, prlid, arg) VALUES (NULL, $userid, $privid, $qarg)");
LJ::statushistory_add($dbh, $userid, $remote->{'userid'}, "privadd", "Granting: \"$privcode\" with arg \"$FORM{'arg'}\"");
$ret .= "Privilege added.<br />\n";
}
else {
my $euser = LJ::ehtml($FORM{'grantuser'});
unless ($userid) {
$ret .= "ERROR: cannot grant priv to non-existent user <b>$euser</b><br />";
}
else { $ret .= "privid is 0!<br />"; }
}
} else {
$ret .= "ERROR: You don't have access to grant <B>$privcode</B> with argument '$arg'.<br />\n";
}
} else {
$ret .= "ERROR: Unknown privilege.<br />\n";
}
} # end if grantuser
}
if ($mode eq "userchange") { $mode = "viewuser"; }
if ($mode eq "privchange") { $mode = "viewpriv"; }
}
if ($mode eq "viewuser")
{
my $user = LJ::canonical_username($FORM{'user'});
my $userid = LJ::get_userid($user);
$ret .= "<h1><a href='./'>&lt;&lt;</a> view user \"$user\"</h1>\n";
unless ($userid) {
$ret .= "<b>Error:</b> non-existent user\n";
return $ret;
}
$ret .= "<form method='post' action='./'>\n";
$ret .= LJ::form_auth();
$ret .= "<input type='hidden' name='mode' value='userchange' />\n";
$ret .= "<input type='hidden' name='user' value='$user' />\n";
$sth = $dbh->prepare("SELECT pm.prmid, pm.prlid, pm.arg FROM priv_map pm, priv_list pl WHERE pm.prlid=pl.prlid AND pm.userid=$userid ORDER BY pl.privcode,pm.arg");
$sth->execute;
$ret .= "<table cellpadding='5' cellspacing='1' border='1'><tr><td><b>Revoke</b></td><td><b>Privilege</b></td><td><b>Arg</b></td></tr>\n";
while (my ($prmid, $prlid, $arg) = $sth->fetchrow_array)
{
my $prec = $priv{$prlid};
my $pcode = $priv{$prlid}->{'privcode'};
my $can_grant = remote_can_grant($remote, $pcode, $arg);
next unless ($prec->{'is_public'} || ($remote && $remote->{'userid'} == $userid) || $can_grant);
$ret .= "<tr><td align='center'>";
if ($can_grant) {
$ret .= "<input type='checkbox' name='revoke:$prmid:$userid' />";
} else {
$ret .= "--";
}
$ret .= "</td><td><a href='./?priv=$pcode'>$pcode</a></td>";
if ($arg)
{
$ret .= "<td><a href='./?priv=$pcode&amp;viewarg=$arg'>$arg</a></td></tr>\n";
} else {
$ret .= "<td>&nbsp;</td></tr>\n";
}
}
$ret .= "</table>";
if (LJ::check_priv($remote, 'admin')) {
$ret .= "<p>Grant <b>$user</b> privilege:<div style='margin-left: 20px;'>\n";
$ret .= "<select name='grantpriv'><option value='' selected='1'></option>";
foreach my $priv (@privs) {
$ret .= "<option value='$priv->{'prlid'}'>$priv->{'privcode'}</option>";
}
$ret .= "</select>\n";
$ret .= "Arg: <input name='arg' size='10' maxlength='40' /></div>\n";
} else {
$ret .= "<p><i>(you do not have access to grant any privileges)</i></p>\n";
}
$ret .= "<p>\n";
if (LJ::check_priv($remote, 'admin')) {
$ret .= "<input type=\"submit\" value=\"Make Changes\" />";
}
$ret .= " <input type=\"submit\" name=\"submit:refresh\" value=\"Just Refresh\" />";
$ret .= "</form>";
return $ret;
}
if ($mode eq "viewpriv") {
my $priv = $pcode2id{$FORM{'priv'}};
my $prec = $priv{$priv};
my $pcode = $prec->{'privcode'};
my $skip = $FORM{'skip'} + 0;
my $limit = 100;
my $viewarg;
if ($FORM{'viewarg'}) {
$viewarg = " AND pm.arg=" . $dbh->quote($FORM{'viewarg'});
}
my $privname = join(' ', grep { $_ } $priv{$priv}->{'privcode'}, $FORM{'viewarg'});
$ret .= "<h1><a href=\"./\">&lt;&lt;</a> view priv \"$privname\"</h1>\n";
$ret .= "<p><b>Privilege Name:</b> $priv{$priv}->{'privname'}";
my ($des, $args) = split(/arg=/, $priv{$priv}->{'des'});
$ret .= "<br /><b>Description:</b> $des" if $des;
$ret .= "<br /><b>Argument:</b> $args" if $args;
$ret .= "</p>";
my ($check_priv, $check_arg) = split("/", $FORM{'viewarg'});
unless ($prec->{'is_public'} || remote_can_grant($remote, $check_priv, $check_arg)) {
$ret .= "<p><b>ERROR:</b> This privilege's access list is not public.</p>\n";
return $ret;
}
$ret .= "<form style='display: inline;' action='./' method='post'>\n";
$ret .= LJ::form_auth();
$ret .= "<p><b>View only privs with arg:</b> ";
$ret .= "<input name='viewarg' size='10' /> ";
$ret .= "<input type='submit' name='submit:load' value='Load' /></p>\n";
$ret .= "<input type='hidden' name='mode' value='privchange' />\n";
$ret .= "<input type='hidden' name='priv' value='$pcode' />";
$sth = $dbh->prepare("SELECT pm.prmid, u.user, u.userid, pm.arg ".
"FROM priv_map pm, useridmap u WHERE pm.prlid=$priv AND pm.userid=u.userid$viewarg ".
"ORDER BY u.user,pm.arg LIMIT $skip,$limit");
$sth->execute;
$ret .= "<table cellpadding='5' cellspacing='1' border='1'><tr><td><b>Revoke</b></td><td><b>User</b></td><td><b>Arg</b></td></tr>\n";
my $showgrant = remote_can_grant($remote, $pcode, $FORM{'viewarg'});
my $foundcount = 0;
while ($_ = $sth->fetchrow_hashref)
{
$foundcount++;
$ret .= "<tr><td align='center'>";
if (remote_can_grant($remote, $priv{$priv}->{'privcode'}, $_->{'arg'}))
{
$ret .= "<input type='checkbox' name=\"revoke:$_->{'prmid'}:$_->{'userid'}\" />";
} else {
$ret .= "--";
}
$ret .= "</td><td><a href=\"./?user=$_->{'user'}\">$_->{'user'}</a></td>";
if ($_->{'arg'} ne "")
{
$ret .= "<td><a href=\"./?priv=$priv{$priv}->{'privcode'}&amp;viewarg=$_->{'arg'}\">$_->{'arg'}</a></td></tr>\n";
} else {
$ret .= "<td>&nbsp;</td></tr>\n";
}
}
$ret .= "<tr><td colspan='3'><b>$foundcount users</b></td></tr>\n";
$ret .= "</table>";
if ($foundcount >= $limit) {
$ret .= "<a href='" . BML::self_link({'skip'=>($skip +$limit)}) . "'>See more...</a>\n";
}
if ($showgrant) {
$ret .= "<p>Grant <b>$privname</b> privilege to:<ul>";
$ret .= "User: <input name='grantuser' size='15' maxlength='15' /> ";
$ret .= "Arg: <input name='arg' size='10' maxlength='40' value='$FORM{'viewarg'}'/></ul>\n";
} else {
$ret .= "<p><i>(you don't have access to grant this privilege to other users)</i></p>\n";
}
if ($showgrant) {
$ret .= "<input name=\"submit:change\" type='submit' value=\"Make Changes\" />\n";
}
$ret .= "</form>\n";
$ret .= "<form style='display: inline;' method='post' action='./'>\n";
$ret .= LJ::form_auth();
$ret .= LJ::html_hidden('mode', 'privchange',
'priv', $pcode,
'viewarg', $FORM{'viewarg'}) . "\n";
$ret .= "<input type='submit' name=\"submit:refresh\" value=\"Just Refresh\" />\n";
$ret .= "</form>\n";
return $ret;
}
return "Unknown mode.";
_code?>
</body>
</html>
<?_c <LJDEP>
lib: cgi-bin/ljlib.pl
link: htdocs/admin/priv/index.bml
post: htdocs/admin/priv/index.bml
</LJDEP _c?>

View File

@@ -0,0 +1,60 @@
<html>
<head><title>Comment view</title>
<body>
<?_code
{
use strict;
use vars qw(%GET %POST);
my $ret;
my $remote = LJ::get_remote();
return "<b>Error:</b> You don't have access to viewing recent comments."
unless LJ::check_priv($remote, "siteadmin", "commentview");
my $user = $GET{'user'};
my $u;
if ($user =~ /^\#(\d+)/) {
$u = LJ::load_userid($1);
} elsif ($user) {
$u = LJ::load_user($user);
}
unless ($u) {
return "<form method='GET'>Username or (#userid) to view comments of: ".
"<input name='user' size='15' /><input type='submit' value='Load' /></form>";
}
$user = $u->{'user'};
$ret .= "<a href='recent_comments.bml'>&lt;&lt;</a> <b>Recent comments of " . LJ::ljuser($u) . "</b> (\#$u->{userid})<br />\n";
my $dbcr = LJ::get_cluster_reader($u);
return "Error: can't get DB for user" unless $dbcr;
my $now = time();
my $sth = $dbcr->prepare("SELECT posttime, journalid, nodetype, nodeid, jtalkid, publicitem ".
"FROM talkleft ".
"WHERE userid=? ORDER BY posttime DESC LIMIT 250");
$sth->execute($u->{'userid'});
my %jcount; # jid -> ct
while (my $r = $sth->fetchrow_hashref) {
$jcount{$r->{'journalid'}}++;
next unless $r->{'nodetype'} eq "L"; # log2 comment
my $ju = LJ::load_userid($r->{'journalid'});
my $lrow = LJ::get_log2_row($ju, $r->{'nodeid'});
my $hr_ago = sprintf("%.1f", ($now - $r->{'posttime'}) / 3600);
if ($lrow) {
my $talkid = ($r->{'jtalkid'} << 8) + $lrow->{'anum'};
my $url = "$LJ::SITEROOT/users/$ju->{user}/$lrow->{ditemid}.html?thread=$talkid\#t$talkid";
$ret .= "$hr_ago hr ago in " . LJ::ljuser($ju) . ": <a href='$url'>$url</a><br />\n";
} else {
$ret .= "$hr_ago hr ago in " . LJ::ljuser($ju) . ": link unavailable<br />";
}
}
return $ret;
}
_code?>
</body>
</html>

View File

@@ -0,0 +1,278 @@
<?_code
use strict;
$title = "Schema Browser";
$body = "";
my $dbh = LJ::get_db_writer();
my $sth;
my $remote = LJ::get_remote();
my $can_doc = 0;
if (LJ::remote_has_priv($remote, "schemadoc")) { $can_doc = 1; }
unless ($can_doc) {
my $url = "/doc/server/ljp.dbschema.ref.html";
$body = "This page is for editing <a href=\"$url\">schema documentation</a>, ";
$body .= "but you don't have the 'schemadoc' priv.";
return;
}
sub magic_links
{
my $des = shift;
$$des =~ s!\[dbtable\[(\w+?)\]\]!<a href="./?mode=viewtable&amp;table=$1">$1</a>!g;
}
if ($FORM{'mode'} eq "")
{
my %table;
$sth = $dbh->prepare("SELECT tablename, public_browsable, des FROM schematables");
$sth->execute;
while (my ($name, $public, $des) = $sth->fetchrow_array) {
$table{$name} = { 'public' => $public, 'des' => $des };
}
$body .= "<?h1 Tables h1?><?p Click a table for more information. p?><p><table cellpadding=4><tr bgcolor=<?emcolor?>><td><b>Table</b></td><td><b>Description</b></td></tr>\n";
$sth = $dbh->prepare("SHOW TABLES");
$sth->execute;
while (my ($table) = $sth->fetchrow_array) {
my $des = $table{$table}->{'des'} || "<i>no description, yet</i>";
magic_links(\$des);
$body .= "<tr valign=top><td nowrap><b><a href=\"./?mode=viewtable&amp;table=$table\">$table</a></b>";
if ($table{$table}->{'public'}) {
$body .= " (<a href=\"./?mode=viewdata&amp;table=$table\">data</a>)";
}
$body .= "</td><td>$des</td></tr>\n";
}
$body .= "</table>\n";
return;
}
if ($FORM{'mode'} eq "viewtable")
{
my $table = $FORM{'table'};
if ($table !~ /^\w+$/) { $body = "Invalid table name!\n"; return; }
my $qtable = $dbh->quote($table);
$sth = $dbh->prepare("SELECT des, public_browsable FROM schematables WHERE tablename=$qtable");
$sth->execute;
my ($tabledes, $browsable) = $sth->fetchrow_array;
$body .= "<a href=\"./\">&lt;&lt; Tables</a>";
if ($browsable) {
$body .= " | <a href=\"./?mode=viewdata&amp;table=$table\">View Data</a>";
}
if ($can_doc) {
$body .= " | <a href=\"./?mode=doc&amp;table=$table\">Edit Documentation</a>";
}
$body .= "<?h1 Table: $table h1?><?p ";
if ($tabledes) {
&magic_links(\$tabledes);
$body .= $tabledes;
} else {
$body .= "Below are the columns and descriptions for the <b>$table</b> table.";
}
$body .= " p?><p>\n";
my %coldes;
$sth = $dbh->prepare("SELECT colname, des FROM schemacols WHERE tablename=$qtable");
$sth->execute;
while (my ($col, $des) = $sth->fetchrow_array) { $coldes{$col} = $des; }
$sth = $dbh->prepare("DESCRIBE $table");
$sth->execute;
$body .= "<table cellpadding=3><tr bgcolor=<?emcolor?>>";
$body .= "<td><b>Key?</b></td>";
$body .= "<td><b>Column</b></td>";
$body .= "<td><b>Type</b></td>";
$body .= "<td><b>Null</b></td>";
$body .= "<td><b>Default</b></td>";
$body .= "<td><b>Description</b></td>";
$body .= "</tr>\n";
while (my $row = $sth->fetchrow_hashref)
{
my $name = $row->{'Field'};
my $type = $row->{'Type'};
my $key = $row->{'Key'};
my $null = $row->{'Null'};
my $def = $row->{'Default'};
my $des = BML::eall($coldes{$name});
magic_links(\$des);
$type =~ s/int\(\d+\)/int/g;
$body .= "<tr valign=top>";
$body .= "<td align=center>$key</td>";
$body .= "<td><b>$name</b></td>";
$body .= "<td>$type</td>";
$body .= "<td align=center>$null</td>";
$body .= "<td align=center>$def</td>";
$body .= "<td>$des</td>";
$body .= "</tr>\n";
}
$body .= "</table>\n";
return;
}
if ($FORM{'mode'} eq "viewdata") {
my $table = $FORM{'table'};
if ($table !~ /^\w+$/) { $body = "Invalid table name!\n"; return; }
my $MAX_ROWS = 100;
$body .= "<a href=\"./\">&lt;&lt; Tables</a><br><a href=\"./?mode=viewtable&amp;table=$table\">&lt;&lt; Table: $table</a><?h1 Data: $table h1?><?p Below are the rows in the <b>$table</b> table. If the table has more than $MAX_ROWS records, only the top $MAX_ROWS are shown. p?><p>\n";
$sth = $dbh->prepare("SELECT tablename, public_browsable, des FROM schematables WHERE tablename='$table'");
$sth->execute;
my ($tablename, $public, $des) = $sth->fetchrow_array;
unless ($public) { $body .= "This table's data is not public.\n"; return; }
$sth = $dbh->prepare("SELECT * FROM $table LIMIT $MAX_ROWS");
$sth->execute;
$body .= "<table cellpadding=3><tr bgcolor=<?emcolor?>>";
foreach my $col (@{$sth->{'NAME'}}) {
$body .= "<td><b>$col</b></td>\n";
}
$body .= "</tr>\n";
while (my $row = $sth->fetchrow_arrayref) {
$body .= "<tr valign=top>\n";
foreach my $val (@$row) {
$body .= "<td>$val</td>\n";
}
$body .= "</tr>\n";
}
$body .= "</table>\n";
return;
}
# show form to enter documentation
if ($FORM{'mode'} eq "doc")
{
unless ($can_doc) { $body .= "You don't have permissions to document the schema."; return; }
my $table = $FORM{'table'};
if ($table !~ /^\w+$/) { $body = "Invalid table name!\n"; return; }
$body .= "<a href=\"./\">&lt;&lt; Tables</a><?h1 Document Table: $table h1?>";
my $qtable = $dbh->quote($table);
my $sth;
$sth = $dbh->prepare("SELECT des FROM schematables WHERE tablename=$qtable");
$sth->execute;
my ($tabledes) = $sth->fetchrow_array;
my %coldes;
$sth = $dbh->prepare("SELECT colname, des FROM schemacols WHERE tablename=$qtable");
$sth->execute;
while (my ($col, $des) = $sth->fetchrow_array) { $coldes{$col} = $des; }
$body .= "<form method=post action=\"./\">\n";
$body .= "<input type=hidden name=table value=\"$table\">\n";
$body .= "<input type=hidden name=mode value=\"docsave\">\n";
$body .= "<p><b>Description:</b><br><textarea name=\"table-des\" rows=10 cols=40 wrap=soft>";
$body .= BML::eall($tabledes) . "</textarea>";
$sth = $dbh->prepare("DESCRIBE $table");
$sth->execute;
$body .= "<p><table cellpadding=3><tr bgcolor=<?emcolor?>>";
$body .= "<td><b>Column</b></td>";
$body .= "<td><b>Type</b></td>";
$body .= "<td><b>Description</b></td>";
$body .= "</tr>\n";
while (my $row = $sth->fetchrow_hashref)
{
my $name = $row->{'Field'};
my $type = $row->{'Type'};
$type =~ s/int\(\d+\)/int/g;
$body .= "<tr valign=top>";
$body .= "<td><b>$name</b></td>";
$body .= "<td>$type</td>";
$body .= "<td><input name=\"col-$name\" size=60 maxlength=255 value=\"" . BML::eall($coldes{$name}) . "\"></td>";
$body .= "</tr>\n";
}
$body .= "</table>\n";
$body .= "<p><input type=submit value=\"Save Changes\"></form>";
return;
}
# save documentation
if ($FORM{'mode'} eq "docsave")
{
unless ($can_doc) { $body .= "You don't have permissions to document the schema."; return; }
my $table = $FORM{'table'};
if ($table !~ /^\w+$/) { $body = "Invalid table name!\n"; return; }
$body .= "<a href=\"./\">&lt;&lt; Tables</a><?h1 Document Table: $table h1?>";
my $qtable = $dbh->quote($table);
my $sth;
$sth = $dbh->prepare("SELECT tablename, des FROM schematables WHERE tablename=$qtable");
$sth->execute;
my ($tablename, $tabledes) = $sth->fetchrow_array;
$FORM{'table-des'} =~ s/\r//;
my $qdes = $dbh->quote($FORM{'table-des'});
if ($tablename) {
# row exists, update.
$dbh->do("UPDATE schematables SET des=$qdes WHERE tablename=$qtable");
} else {
# no row exists, so insert
$dbh->do("INSERT INTO schematables (tablename, public_browsable, des) VALUES ($qtable, '0', $qdes)");
}
if ($dbh->err) { $body .= $dbh->errstr; return; }
my %olddes;
my %newdes;
### load old descriptions
$sth = $dbh->prepare("SELECT colname, des FROM schemacols WHERE tablename=$qtable");
$sth->execute;
while (my ($col, $des) = $sth->fetchrow_array) { $olddes{$col} = $des; }
### check new descriptions (only for valid columns)
$sth = $dbh->prepare("DESCRIBE $table");
$sth->execute;
if ($dbh->err) { $body .= $dbh->errstr; return; }
while (my $row = $sth->fetchrow_hashref)
{
my $name = $row->{'Field'};
my $type = $row->{'Type'};
$FORM{"col-$name"} =~ s/\r//;
if ($FORM{"col-$name"} ne $olddes{$name}) {
$newdes{$name} = $FORM{"col-$name"};
}
}
if (%newdes) {
my $sql = "REPLACE INTO schemacols (tablename, colname, des) VALUES ";
foreach my $col (keys %newdes) {
my $qcol = $dbh->quote($col);
my $qdes = $dbh->quote($newdes{$col});
$sql .= "($qtable, $qcol, $qdes),";
}
chop $sql;
$dbh->do($sql);
if ($dbh->err) { $body .= "[3] ($sql)<p>" . $dbh->errstr; return; }
}
$body .= "<?h1 Success h1?><?p Documentation saved. <a href=\"./?mode=viewtable&amp;table=$table\">View</a>. p?>";
return;
}
return;
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?><?_c <LJDEP>
link: htdocs/admin/scheme/index.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,276 @@
<?_code
{
use strict;
use vars qw($title $body %GET %POST);
$title = "Spam Reports";
$body = "";
my $error = sub {
$title = "Error";
$body = join '', @_;
return undef;
};
my $makelink = sub {
my ($by, $what, $state, $reporttime, $etext) = @_;
$by = LJ::eurl($by);
$what = LJ::eurl($what);
$state = LJ::eurl($state);
$reporttime = LJ::mysql_time($reporttime);
$reporttime = $etext if $etext;
return "<a href=\"spamreports.bml?mode=view&amp;by=$by&amp;what=$what&amp;state=$state\">$reporttime</a>";
};
my $dellink = sub {
my ($by, $what, $text) = @_;
return "<form method=\"post\" action=\"spamreports.bml\">" .
LJ::html_hidden('mode', $by) .
LJ::html_hidden('what', $what) .
LJ::html_hidden('ret', "spamreports.bml?" . join('&amp;', map { "$_=" . LJ::eurl($GET{$_}) } keys %GET)) .
LJ::html_submit('submit', $text) .
"</form>";
};
# login check
my $remote = LJ::get_remote();
return $error->("You must be logged in to be here.")
unless $remote;
# priv check
return $error->("You do not have the necessary privilege to be here.")
unless LJ::check_priv($remote, 'siteadmin', 'spamreports');
# show the top 10 spam reports by IP
my $mode = lc($GET{mode} || $POST{mode});
$mode = '' if $mode =~ /^del/ && !LJ::did_post() && !LJ::check_referer('/admin/spamreports.bml');
# combined/user/anon viewing?
my $view = $mode =~ /_([cua])$/ ? $1 : 'c';
my ($extrawhere, $extratitle);
if ($view eq 'c') { $extrawhere = '1'; }
elsif ($view eq 'u') { $extrawhere = 'posterid > 0'; $extratitle = " - Users Only"; }
elsif ($view eq 'a') { $extrawhere = 'posterid = 0'; $extratitle = " - Anonymous Only"; }
$mode =~ s/_[cua]$//; # strip out viewing option
my $dbr = LJ::get_db_reader();
return $error->("Unable to get database reader handle.") unless $dbr;
my @rows;
my @headers;
if ($mode eq 'top10ip') {
# top 10 by ip
$title = "Spam Reports - Top 10 by IP Address";
@headers = ('Number of Reports', 'IP Address', 'Most Recent Report');
my $res = $dbr->selectall_arrayref('SELECT COUNT(ip) AS num, ip, MAX(reporttime) FROM spamreports ' .
"WHERE state = 'open' AND ip IS NOT NULL " .
'GROUP BY ip ORDER BY num DESC LIMIT 10');
foreach (@$res) {
push @rows, [ $_->[0], $_->[1], $makelink->('ip', $_->[1], 'open', $_->[2]) ];
}
} elsif ($mode eq 'top10user') {
# top 10 by user
$title = "Spam Reports - Top 10 by User";
@headers = ('Number of Reports', 'Posted By User', 'Most Recent Report');
my $res = $dbr->selectall_arrayref('SELECT COUNT(posterid) AS num, posterid, MAX(reporttime) FROM spamreports ' .
"WHERE state = 'open' AND posterid > 0 " .
'GROUP BY posterid ORDER BY num DESC LIMIT 10');
foreach (@$res) {
my $u = LJ::load_userid($_->[1]);
push @rows, [ $_->[0], LJ::ljuser($u), $makelink->('posterid', $_->[1], 'open', $_->[2]) ];
}
} elsif ($mode eq 'tlast10') {
# most recent 10 reports
$title = "Spam Reports - Last 10$extratitle";
@headers = ('Posted By', 'Posted In', 'Report Time');
my $res = $dbr->selectall_arrayref('SELECT posterid, ip, journalid, reporttime FROM spamreports ' .
"WHERE state = 'open' AND $extrawhere ORDER BY reporttime DESC LIMIT 10");
foreach (@$res) {
my $u2 = LJ::load_userid($_->[2]);
if ($_->[0] > 0) {
my $u = LJ::load_userid($_->[0]);
push @rows, [ LJ::ljuser($u), LJ::ljuser($u2), $makelink->('posterid', $_->[0], 'open', $_->[3]) ];
} else {
push @rows, [ "$_->[1]", LJ::ljuser($u2), $makelink->('ip', $_->[1], 'open', $_->[3]) ];
}
}
} elsif ($mode =~ /^last(\d+)hr$/) {
# reports in last X hours
my $hours = $1+0;
my $secs = $hours * 3600; # seconds in an hour
$title = "Spam Reports - Last $hours Hour" . ($hours == 1 ? '' : 's') . $extratitle;
@headers = ('Number of Reports', 'Posted By', 'Report Time');
my $res = $dbr->selectall_arrayref('SELECT journalid, ip, posterid, reporttime FROM spamreports ' .
"WHERE $extrawhere AND reporttime > (UNIX_TIMESTAMP() - $secs) LIMIT 1000");
# count up items and their most recent report
my %hits;
my %times;
foreach (@$res) {
my $key;
if ($_->[2] > 0) {
my $u = LJ::load_userid($_->[2]);
next unless $u;
$key = $u->{userid};
} else {
next unless $_->[1];
$key = $_->[1];
}
$hits{$key}++;
$times{$key} = $_->[3] unless $times{$key} gt $_->[3];
}
# now reverse to number => item list
my %revhits;
foreach (keys %hits) {
if ($revhits{$hits{$_}}) {
push @{$revhits{$hits{$_}}}, $_;
} else {
$revhits{$hits{$_}} = [ $_ ];
}
}
# now push them onto @rows
foreach (sort { $b <=> $a } keys %revhits) {
my $r = $revhits{$_};
foreach (@$r) {
my $isip = $_ =~ /\./ ? 1 : 0;
push @rows, [ $hits{$_}, $isip ? $_ : LJ::ljuser(LJ::load_userid($_)),
$makelink->($isip ? 'ip' : 'posterid', $_, 'open', $times{$_}) ];
}
}
} elsif ($mode eq 'view') {
# view a particular report
my ($by, $what, $state) = (lc($GET{by}), $GET{what}, lc($GET{state}));
$by = '' unless $by =~ /^(?:ip|poster(?:id)?)$/;
$state = 'open' unless $state =~ /^(?:open|closed)$/;
$body .= "<?p [ <a href=\"spamreports.bml\">&lt;&lt; Front Page</a> ] ";
# open/closed links
my $eargs = LJ::eurl("?by=$by&what=$what&state");
if ($state eq 'open') {
$body .= " [ " . $makelink->($by, $what, 'closed', undef, "View Closed Reports") . " ]";
} else {
$body .= " [ " . $makelink->($by, $what, 'open', undef, "View Open Reports") . " ]";
}
$body .= " p?>\n";
# setup title and verify that the data is right
if ($by eq 'posterid') {
$what += 0;
my $u = LJ::load_userid($what);
return $error->('No such posterid.') unless $u;
$title = "Spam Reports - By $u->{user} ($state)";
} elsif ($by eq 'poster') {
my $u = LJ::load_user($what);
return $error->('No such user.') unless $u;
$title = "Spam Reports - Comments By $u->{user}";
# Now just pretend that user used 'posterid'
$by = 'posterid';
$what = $u->{userid};
} elsif ($by eq 'ip') {
# check for right format x.x.x.x, not necessarily a valid IP
return $error->('No such IP.') if $what !~ /^\d+\.\d+\.\d+\.\d+$/ or length $what > 15;
$title = "Spam Reports - By IP $what ($state)";
}
# see if we should call a hook for extra actions?
$body .= LJ::run_hook('spamreport_notification', $remote, { $by => $what })
if $state eq 'open' && $by eq 'posterid';
# now the general info gathering
my $res = $dbr->selectall_arrayref('SELECT reporttime, journalid, subject, body, ip, posttime, report_type ' .
"FROM spamreports WHERE state=? AND $by=? ORDER BY reporttime DESC LIMIT 1000",
undef, $state, $what);
unless ($res && @$res) {
$body .= "No reports found.";
return undef;
}
$body .= '<table>';
foreach (@$res) {
my $u2 = LJ::load_userid($_->[1]);
my $x = $by eq 'ip' ? 4 : 1;
my $comment_body = $_->[3];
LJ::text_uncompress(\$comment_body);
my $spamlocation = ($_->[6] eq 'entry') ? 'Entry' : 'Comment';
$body .= '<tr><td>' . ($state eq 'open' ? $dellink->("del$by", "$_->[0]:$_->[$x]", 'Close') : '') . '</td><td>' .
"<strong>$spamlocation in:</strong> " . LJ::ljuser($u2) . '<br />' .
'<strong>Report Time:</strong> ' . LJ::mysql_time($_->[0]) . '<br />' .
"<strong>$spamlocation Time:</strong> " . ($_->[5] ? LJ::mysql_time($_->[5]) : 'not recorded') . '<br />' .
'<strong>Subject:</strong> ' . LJ::ehtml($_->[2] || 'no subject') . '<br />' .
'<strong>Body:</strong> ' . LJ::ehtml($comment_body || 'no body') . '<br />' .
'</td></tr><tr><td>&nbsp;</td></tr>';
}
$body .= "</table><br />" . ($state eq 'open' ? $dellink->("delby$by", $what, 'Close All') : '');
} elsif ($mode =~ /^del/) {
# figure out our combination
my $dbh = LJ::get_db_writer();
return $error->("Unable to get database writer handle.") unless $dbh;
my ($sql, $count, $backlink);
if ($mode =~ /^delby/) {
# enmasse deletion
my $where = $mode =~ /ip$/ ? 'ip' : 'posterid';
$sql = "UPDATE spamreports SET state='closed' WHERE $where=?";
$count = $dbh->do($sql, undef, $POST{what});
return $error->($dbh->errstr) if $dbh->err;
} else {
# single item deletion
my $where = $mode =~ /ip$/ ? 'ip' : 'journalid';
my ($time, $data) = ($1, $2)
if $POST{what} =~ /^(\d+):(.+)$/;
$data ||= $POST{what};
$count = $dbh->do("UPDATE spamreports SET state='closed' WHERE reporttime=? AND $where=? AND state='open'", undef, $time, $data);
return $error->($dbh->errstr) if $dbh->err;
$backlink = "[ <a href='$POST{ret}'>&lt;&lt; Go Back</a> ]";
}
$title = "Close Reports";
$body .= "<?p [ <a href=\"spamreports.bml\">&lt;&lt; Front Page</a> ] $backlink p?>\n";
my $s = $count == 1 ? '' : 's';
$body .= "Closed $count report$s.\n";
} else {
# standard
my %modes = (top10user => 'Top 10 by User', top10ip => 'Top 10 by IP Address', tlast10 => 'Last 10 Reports',
last01hr => 'Last 1 Hour', last06hr => 'Last 6 Hours', last24hr => 'Last 24 Hours');
$body .= "<?p Available reports: p?>\n<ul>";
foreach (sort keys %modes) {
$body .= "<li><a href=\"spamreports.bml?mode=$_\">$modes{$_}</a>";
if ($_ =~ /last/) {
# this is a last view, so we have other options
$body .= " [<a href=\"spamreports.bml?mode=${_}_u\">users</a>, ";
$body .= "<a href=\"spamreports.bml?mode=${_}_a\">anonymous</a>]";
}
$body .= "</li>";
}
$body .= qq{<li><form method="GET" action="spamreports.bml" style="display: inline; margin: 0;">
<label for="repu">Reports for user:
<input type="text" name="what" size="15" maxlength="15" id="repu" />
<input type="hidden" name="by" value="poster" />
<input type="hidden" name="mode" value="view" />
</label></form></li>
};
$body .= "</ul>\n<?p Please select one of the above reports to view. Actions can be taken when viewing a report. p?>";
}
# now spit out the requested table
return unless @headers;
$body .= "<?p [ <a href=\"spamreports.bml\">&lt;&lt; Front Page</a> ] p?>";
$body .= "<table width=\"50%\">\n<tr>";
$body .= "<th align=\"center\">$_</th>" foreach @headers;
$body .= "</tr>\n";
foreach (@rows) {
$body .= "<tr>";
$body .= "<td align=\"center\">$_</td>" foreach @$_;
$body .= "</tr>\n";
}
$body .= "</table>\n";
return;
}
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?>

View File

@@ -0,0 +1,122 @@
<html>
<head><title>Status History</title></head>
<body>
<?_code
{
use strict;
use vars qw(%FORM);
my $dbr = LJ::get_db_reader();
# check privs
my $remote = LJ::get_remote();
unless (LJ::check_priv($remote, "historyview")) {
return "Sorry, you don't have access to view this page.";
}
my $ret;
$ret .= "Fill in at least one field below:";
$ret .= "<form method='post' action='statushistory.bml'>\n";
$ret .= LJ::html_hidden('orderby', $FORM{'orderby'}, 'flow', $FORM{'flow'}) . "\n";
$ret .= "User: " . LJ::html_text({ 'name' => 'user', 'size' => '15', 'maxlength' => '15' }) . "\n";
$ret .= "Admin: " . LJ::html_text({ 'name' => 'admin', 'size' => '15', 'maxlength' => '15' }) . "\n";
$ret .= "Type: " . LJ::html_text({ 'name' => 'type', 'size' => '20', 'maxlength' => '20' }) . "\n";
$ret .= LJ::html_submit('query_submit', 'Search');
$ret .= "</form>\n\n";
return $ret unless ($FORM{'user'} || $FORM{'admin'} || $FORM{'type'});
$ret .= "<hr size='1'>\n\n";
# build query
my @where;
if ($FORM{'user'} ne "") {
my $userid = LJ::get_userid($FORM{'user'});
unless ($userid) { return "unknown user"; }
push @where, "s.userid=$userid";
}
if ($FORM{'admin'} ne "") {
my $userid = LJ::get_userid($FORM{'admin'});
unless ($userid) { return "unknown admin"; }
push @where, "s.adminid=$userid";
}
if ($FORM{'type'} ne "") {
my $qt = $dbr->quote($FORM{'type'});
push @where, "s.shtype=$qt";
}
my $where = "WHERE " . join(" AND ", @where) . " " if @where;
my $orderby = 'shdate';
foreach (qw(user admin shdate shtype notes)) {
$orderby = "u.$_", next if $FORM{'orderby'} eq $_ && $_ eq 'user';
$orderby = "ua.$_", next if $FORM{'orderby'} eq $_ && $_ eq 'admin';
$orderby = "s.$_" if $FORM{'orderby'} eq $_;
}
my $flow = $FORM{'flow'} eq 'asc' ? 'ASC' : 'DESC';
my $sth = $dbr->prepare("SELECT u.user, ua.user AS admin, s.shtype, s.shdate, s.notes " .
"FROM statushistory s " .
"LEFT JOIN useridmap ua ON s.adminid=ua.userid " .
"LEFT JOIN useridmap u ON s.userid=u.userid " .
$where .
"ORDER BY $orderby $flow LIMIT 1000");
$sth->execute;
return $dbr->errstr if $dbr->err;
# column headings w/ sort links
$ret .= "<p><b>Query:";
foreach (qw(user admin type)) {
$ret .= "&nbsp;&nbsp;$_=" . LJ::eall($FORM{$_}) if $FORM{$_}
}
$ret .= "</b></p>\n";
$ret .= "<table border='1' cellpadding='5' width='100%'>\n<tr>";
foreach (qw(user admin shtype shdate notes)) {
my $link = "statushistory.bml?user=$FORM{'user'}&admin=$FORM{'admin'}&type=$FORM{'type'}&orderby=$_";
$link .= $FORM{'orderby'} eq $_ && $FORM{'flow'} eq 'asc' ? "&flow=desc" : "&flow=asc";
$ret .= "<td><b><a href='$link'>$_</a></b></td>";
}
$ret .= "</tr>\n";
# query built above
my $ct = 0;
while (my $hist = $sth->fetchrow_hashref) {
# see if they can see this item: either they have unarged historyview or
# they have historyview:shtype
next unless LJ::check_priv($remote, 'historyview', '') ||
LJ::check_priv($remote, 'historyview', $hist->{shtype});
$ret .= "<tr>";
foreach (qw(user admin shtype shdate notes)) {
$ret .= "<td>";
if ($hist->{$_} && ($_ eq 'user' || $_ eq 'admin')) {
$ret .= LJ::ljuser($hist->{$_});
} elsif ($_ eq 'notes') {
# notes need to be ehtml'd, but afterwards, we can convert \n to <br />
my $enotes = LJ::ehtml($hist->{$_});
$enotes =~ s!\n!<br />\n!g;
$ret .= $enotes;
} else {
$ret .= LJ::ehtml($hist->{$_});
}
$ret .= "</td>";
}
$ret .= "</tr>\n";
$ct++;
}
$ret .= "<tr><td colspan='5'><b>$ct rows in set";
$ret .= "[truncated]" if $ct >= 1000;
$ret .= "</b></td></tr>\n";
$ret .= "</table>\n\n";
return $ret;
}
_code?>
</body>
</html>

View File

@@ -0,0 +1 @@
&nbsp;

View File

@@ -0,0 +1,34 @@
<?page
title=>Topic Directory Administration
body<=
<?_code
use strict;
my $ret;
my $remote = LJ::get_remote();
if (LJ::remote_has_priv($remote, "topicaddtopic")) {
$ret .= "<P><A HREF=\"screentop.bml\"><B>Screen Topic Submissions</B></A> that are awaiting approval into a category.";
}
if (LJ::remote_has_priv($remote, "topicscreencat")) {
$ret .= "<P><A HREF=\"screen.html\"><B>Screen Entry Submissions</B></A> that are awaiting approval into a topic.";
}
unless ($ret) {
$ret .= "You have no administrative priviledges in this area, or you are not logged in.";
}
return $ret;
_code?>
<=body
page?><?_c <LJDEP>
link: htdocs/admin/topics/screentop.bml, htdocs/admin/topics/screen.html
</LJDEP> _c?>

View File

@@ -0,0 +1,30 @@
<?_code
return "This page is old and uses a horrendous database query. It won't likely return.";
my $user = $FORM{'user'};
my $dbh = LJ::get_db_writer();
my $sth = $dbh->prepare("SELECT styleid, user, styledes, type, is_embedded, is_colorfree FROM style WHERE is_public='Y' ORDER BY user");
$sth->execute;
my $ret = "";
my $last = "";
while ($sty = $sth->fetchrow_hashref) {
if ($last ne $sty->{'user'}) {
$last = $sty->{'user'};
$ret .= "<B>$last</B><BR>\n";
}
if ($user) {
$ret .= "&nbsp;&nbsp;- <A TARGET=\"main\" HREF=\"/customview.cgi?styleid=$sty->{'styleid'}&amp;user=$user\">$sty->{'styledes'}</A><BR>\n";
} else {
$ret .= "&nbsp;&nbsp;- <A TARGET=\"main\" HREF=\"styleinfo.bml?styleid=$sty->{'styleid'}\">$sty->{'styledes'}</A><BR>\n";
}
}
return $ret;
_code?><?_c <LJDEP>
link: htdocs/customview.cgi, htdocs/admin/topics/styleinfo.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,17 @@
<HTML>
<HEAD>
<TITLE>Topic Directory Administration</TITLE>
</HEAD>
<FRAMESET COLS="150,100%">
<FRAME NAME="links" SRC="screen_links.bml">
<FRAME NAME="main" SRC="screen_instructions.bml">
</FRAMESET>
</HTML>
<!--
<?_c <LJDEP>
post: htdocs/admin/topics/screen_links.bml, htdocs/admin/topics/screen_instructions.bml
</LJDEP> _c?>
-->

View File

@@ -0,0 +1,50 @@
<?_code
use strict;
use vars qw(%FORM);
my $remote = LJ::get_remote();
return $ML{'error.noremote'} unless $remote;
return "You don't have access to do this."
unless LJ::remote_has_priv($remote, "topicscreencat");
my $dbh = LJ::get_db_writer();
my $ret;
foreach my $field (keys %FORM)
{
my $act = $FORM{$field};
next if ($act eq "L");
next unless ($field =~ /^action:(\d+):(\d+)$/);
my $topid = $1;
my $itemid = $2;
if ($act eq "A") {
my $sth = $dbh->prepare("UPDATE topic_map SET status='on', screendate=NOW(), " .
"screenuserid=? WHERE tptopid=? AND itemid=? AND status='new'");
$sth->execute($remote->{'userid'}, $topid, $itemid);
if ($sth->rows) {
$ret .= "<b>$itemid</b> approved.<br />\n";
} else {
$ret .= "<b>$itemid</b> already acted on.<br />\n";
}
}
if ($act eq "D") {
my $sth = $dbh->prepare("UPDATE topic_map SET status='deny', screendate=NOW(), " .
"screenuserid=? WHERE tptopid=? AND itemid=? AND status='new'");
$sth->execute($remote->{'userid'}, $topid, $itemid);
if ($sth->rows) {
$ret .= "<B>$itemid</B> denied.<BR>\n";
} else {
$ret .= "<B>$itemid</B> already acted on.<BR>\n";
}
}
}
return $ret;
_code?><?_c <LJDEP>
# None
</LJDEP> _c?>

View File

@@ -0,0 +1,6 @@
Click an item on the left and it will appear here. Read it, decide if it's applicable to the category, and then select "Approve" or "Deny".
<!--
<?_c <LJDEP>
# None
</LJDEP> _c?>
-->

View File

@@ -0,0 +1,106 @@
<?_code
my ($ret, $sth);
my $remote = LJ::get_remote();
my %cataccess;
unless (LJ::remote_has_priv($remote, "topicscreencat", \%cataccess)) {
return "You don't have access to do this, or you're not logged in.";
}
my $dbh = LJ::get_db_writer();
my $and_cat_in = "";
unless ($cataccess{'all'}) {
my $in = join(", ", map { $dbh->quote($_); } keys %cataccess);
$and_cat_in = "AND tl.tpcatid IN ($in)";
}
$sth = $dbh->prepare("SELECT tm.tpmapid, tm.tptopid, tm.itemid FROM topic_map tm, topic_list tl WHERE tm.tptopid=tl.tptopid AND tm.status='new' $and_cat_in LIMIT 50");
$sth->execute;
if ($dbh->err) { return $dbh->errstr; }
my %topic;
while (my $map = $sth->fetchrow_hashref)
{
push @maps, $map;
$topic{$map->{'tptopid'}} = undef;
}
unless (@maps) {
return "<B>Empty!</B> ... no items are awaiting approval";
}
my $top_in = join(",", keys %topic);
$sth = $dbh->prepare("SELECT tptopid, tpcatid, topname FROM topic_list WHERE tptopid IN ($top_in)");
$sth->execute;
if ($dbh->err) { return $dbh->errstr; }
my %cat;
while (my $top = $sth->fetchrow_hashref)
{
$topic{$top->{'tptopid'}} = $top;
$cat{$top->{'tpcatid'}} = undef;
}
my $cat_in = join(",", keys %cat);
$sth = $dbh->prepare("SELECT tpcatid, parent, catname FROM topic_cats WHERE tpcatid IN ($cat_in)");
$sth->execute;
if ($dbh->err) { return $dbh->errstr; }
while (my $cat = $sth->fetchrow_hashref)
{
$cat{$cat->{'tpcatid'}} = $cat;
}
$ret .= "<FORM METHOD=POST ACTION=\"screen_do.bml\">";
foreach my $map (@maps)
{
my $catid = $topic{$map->{'tptopid'}}->{'tpcatid'};
next unless ($cataccess{'all'} || $cataccess{$catid});
&load_cats_up($catid, \%cat);
$ret .= "<P>";
my $fullcat;
my $catup = $catid;
while ($catup) {
$fullcat = "$cat{$catup}->{'catname'} : $fullcat";
$catup = $cat{$catup}->{'parent'};
}
$fullcat .= $topic{$map->{'tptopid'}}->{'topname'};
$ret .= "<B><FONT SIZE=-1>[$fullcat]</FONT></B>";
$ret .= "<BR><A HREF=\"/talkread.bml?itemid=$map->{'itemid'}\" TARGET=\"main\">$map->{'itemid'}</A>";
my %opts = ("L" => "Leave", "A" => "Approve", "D" => "Deny");
foreach (qw(L A D)) {
$ret .= "<BR><INPUT TYPE=RADIO NAME=\"action:$map->{'tptopid'}:$map->{'itemid'}\" VALUE=\"$_\">$opts{$_}\n";
}
}
$ret .= "<P><INPUT TYPE=SUBMIT VALUE=\"Submit\"></FORM>";
return $ret;
sub load_cats_up
{
my $catid = shift;
my $hashref = shift;
$catid += 0;
while ($catid)
{
unless ($hashref->{$catid}) {
$sth = $dbh->prepare("SELECT parent, catname FROM topic_cats WHERE tpcatid=$catid");
$sth->execute;
my $cat = $sth->fetchrow_hashref;
if ($cat) {
$hashref->{$catid} = $cat;
$catid = $cat->{'parent'}
}
} else {
$catid = $hashref->{$catid}->{'parent'};
}
}
}
_code?><?_c <LJDEP>
link: htdocs/talkpost.bml
post: htdocs/admin/topics/screen_links.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,87 @@
<?page
title=>Screen Topics
body<=
<?_code
my $dbh = LJ::get_db_writer();
my ($ret, $sth);
my $remote = LJ::get_remote();
my %cataccess;
unless (LJ::remote_has_priv($remote, "topicscreencat", \%cataccess)) {
return "You don't have access to do this, or you're not logged in.";
}
my $and_cat_in = "";
unless ($cataccess{'all'}) {
my $in = join(", ", map { $dbh->quote($_); } keys %cataccess);
$and_cat_in = "AND tpcatid IN ($in)";
}
$sth = $dbh->prepare("SELECT tptopid, tpcatid, topname FROM topic_list WHERE status='new' $and_cat_in");
$sth->execute;
if ($dbh->err) { return $dbh->errstr; }
@new = ();
push @new, $_ while ($_ = $sth->fetchrow_hashref);
unless (@new) {
return "<B>Empty!</B> ... no topics are awaiting approval";
}
$ret .= "<FORM METHOD=POST ACTION=\"screentop_do.bml\">";
my $count = 0;
foreach my $new (@new)
{
next if (++$count > 50);
&load_cats_up($new->{'tpcatid'}, \%cat);
$ret .= "<P>";
my $fullcat;
my $catup = $new->{'tpcatid'};
while ($catup) {
$fullcat = "$cat{$catup}->{'catname'} : $fullcat";
$catup = $cat{$catup}->{'parent'};
}
$fullcat =~ s/\s+:\s+$//;
$ret .= "<B><FONT SIZE=-1>[$fullcat]</FONT></B>";
$ret .= "<BR>$new->{'topname'}";
my %opts = ("L" => "Leave", "A" => "Approve", "D" => "Deny");
foreach (qw(L A D)) {
$ret .= "<BR><INPUT TYPE=RADIO NAME=\"action:$new->{'tptopid'}\" VALUE=\"$_\">$opts{$_}\n";
}
}
$ret .= "<P><INPUT TYPE=SUBMIT VALUE=\"Submit\"></FORM>";
return $ret;
sub load_cats_up
{
my $catid = shift;
my $hashref = shift;
$catid += 0;
while ($catid)
{
unless ($hashref->{$catid}) {
$sth = $dbh->prepare("SELECT parent, catname FROM topic_cats WHERE tpcatid=$catid");
$sth->execute;
my $cat = $sth->fetchrow_hashref;
if ($cat) {
$hashref->{$catid} = $cat;
$catid = $cat->{'parent'}
}
} else {
$catid = $hashref->{$catid}->{'parent'};
}
}
}
_code?>
<=body
page?><?_c <LJDEP>
post: htdocs/admin/topics/screentop_do.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,18 @@
<HTML>
<HEAD>
<TITLE>Topic Directory Administration</TITLE>
</HEAD>
<FRAMESET COLS="150,100%">
<FRAME NAME="links" SRC="screen_links.bml">
<FRAME NAME="main" SRC="screen_instructions.bml">
</FRAMESET>
</HTML>
<!--
<?_c <LJDEP>
link: htdocs/admin/topics/screen_links.bml, htdocs/admin/topics/screen_instructions.bml
</LJDEP> _c?>
-->

View File

@@ -0,0 +1,49 @@
<?_code
my $remote = LJ::get_remote();
unless (LJ::remote_has_priv($remote, "topicaddtopic")) {
return "You don't have access to do this, or you're not logged in.";
}
my $dbh = LJ::get_db_writer();
my $sth;
my $ret;
foreach my $field (keys %FORM)
{
next unless ($field =~ /^action:(\d+)$/);
my $act = $FORM{$field};
next if ($act eq "L");
my $topid = $1;
if ($act eq "A") {
$sth = $dbh->prepare("UPDATE topic_list SET status='on' WHERE tptopid=$topid AND status='new'");
$sth->execute;
if ($sth->rows) {
$ret .= "<B>$topid</B> approved.<BR>\n";
} else {
$ret .= "<B>$topid</B> already acted on.<BR>\n";
}
}
if ($act eq "D") {
$sth = $dbh->prepare("UPDATE topic_list SET status='deny' WHERE tptopid=$topid AND status='new'");
$sth->execute;
if ($sth->rows) {
$ret .= "<B>$topid</B> denied.<BR>\n";
} else {
$ret .= "<B>$topid</B> already acted on.<BR>\n";
}
}
}
return $ret;
_code?><?_c <LJDEP>
# None
</LJDEP> _c?>

View File

@@ -0,0 +1,12 @@
<FORM TARGET="links" ACTION="links.bml" METHOD=GET>
Username to preview:
<INPUT NAME="user" SIZE=15 MAXLENGTH=15>
<P><INPUT TYPE="SUBMIT" VALUE="Make links">
</FORM>
<!--
<?_c <LJDEP>
post: htdocs/admin/topics/links.bml
</LJDEP> _c?>
-->

View File

@@ -0,0 +1,111 @@
<?page
title=>User Log Viewer
head<=
<style>
<!--
td.logrow {
border: solid 1px rgb(230,230,230);
padding: 2px;
margin: 0px;
}
th.logrow {
border: solid 1px rgb(180,180,180);
padding: 2px;
margin: 0px;
text-weight: bold;
}
-->
</style>
<=head
body<=
<?_code
{
use strict;
use vars qw($GET $POST);
my $remote = LJ::get_remote();
return "<?needlogin?>" unless $remote;
my $err = sub {
return "<?h1 Error h1?><?p $_[0] p?>";
};
return $err->("You do not have the necessary privilege to view this page.")
unless LJ::check_priv($remote, 'canview', 'userlog') ||
LJ::check_priv($remote, 'canview', '*');
my $user = LJ::canonical_username($POST{user});
my $ret = <<FORM;
<form method='post' action='userlog.bml'>
Username: <input type='text' name='user' value='$user' maxlength='15' size='15' /> <input type='submit' value='View' />
</form>
FORM
return $ret unless $user;
my $u = LJ::load_user($user);
return $err->("User does not exist.")
unless $u;
my $dbcr = LJ::get_cluster_reader($u);
return $err->("Unable to get user cluster reader.")
unless $dbcr;
my $sth = $dbcr->prepare('SELECT * FROM userlog WHERE userid = ? ORDER BY logtime DESC LIMIT 1000');
$sth->execute($u->{userid});
return $err->("Database error: " . $sth->errstr)
if $sth->err;
$ret .= "<?p Latest log entries for " . LJ::ljuser($u) . ". p?>";
$ret .= "<table style='border: solid 1px black; width: 95%;'>\n";
$ret .= "<tr>";
$ret .= join('', map { "<th class='logrow'>$_</th>" } ("Date and Time", "Action", "Initiator", "IP Address", "Uniq Cookie"));
$ret .= "</tr>\n";
while (my $row = $sth->fetchrow_hashref) {
my $extra = {};
LJ::decode_url_string($row->{extra}, $extra);
my $action = "Action undefined for: $row->{action}";
if ($row->{action} eq 'delete_entry') {
$action = "Deleted entry $row->{actiontarget} via $extra->{method}";
} elsif ($row->{action} eq 'account_create') {
$action = "Account created";
} elsif ($row->{action} eq 'ban_set') {
my $u = LJ::load_userid($row->{actiontarget});
$action = "Banned " . LJ::ljuser($u) if $u;
} elsif ($row->{action} eq 'ban_unset') {
my $u = LJ::load_userid($row->{actiontarget});
$action = "Unbanned " . LJ::ljuser($u) if $u;
} elsif ($row->{action} eq 'maintainer_add') {
my $u = LJ::load_userid($row->{actiontarget});
$action = "Added maintainer " . LJ::ljuser($u) if $u;
} elsif ($row->{action} eq 'maintainer_remove') {
my $u = LJ::load_userid($row->{actiontarget});
$action = "Removed maintainer " . LJ::ljuser($u) if $u;
} else {
$action = "Unknown action ($row->{action})";
}
my $time = LJ::mysql_time($row->{logtime});
my $actor;
if ($row->{remoteid}) {
my $u = LJ::load_userid($row->{remoteid});
$actor = LJ::ljuser($u);
} else {
$actor = "<em>not recorded</em>";
}
my $ip = $row->{ip} || "<em>not recorded</em>";
my $uniq = $row->{uniq} || "<em>not recorded</em>";
$ret .= "<tr>" . join('', map { "<td class='logrow'>$_</td>" } ($time, $action, $actor, $ip, $uniq)) . "</tr>\n";
}
$ret .= "</table>";
return $ret;
}
_code?>
<=body
page?>

184
livejournal/htdocs/allpics.bml Executable file
View File

@@ -0,0 +1,184 @@
<?_code
{
use strict;
use vars qw(%GET $title $body $head);
$title = "$ML{'.title'}";
$body = "";
$head = "";
my $user = LJ::canonical_username($GET{'user'});
my $remote = LJ::get_remote();
my $u = ! $user || $remote && $remote->{'user'} eq $user ? $remote : LJ::load_user($user);
unless ($u) {
$body = "<?h1 $ML{'Error'} h1?><?p $ML{'.error.noparam'} p?>";
return;
}
LJ::load_user_props($u, "opt_blockrobots") if $u->{'statusvis'} eq 'V';
if ($u->{'statusvis'} ne 'V' || $u->{'opt_blockrobots'}) {
$head .= LJ::robot_meta_tags();
}
# no need for viewsome, due to the fact that none of this is private anyway. just
# allow anybody with any version of viewall to see userpics for non-V statusvis users
if ($GET{viewall} && LJ::check_priv($remote, 'canview')) {
LJ::statushistory_add($u->{'userid'}, $remote->{'userid'},
"viewall", "allpics: $u->{'user'}, statusvis: $u->{'statusvis'}");
} else {
if ($u->{'statusvis'} eq "S") {
$title = $ML{'error.suspended.title'};
$body = "<?h1 $ML{'error.suspended.name'} h1?><?p " .
BML::ml('error.suspended.text', { 'user' => LJ::ljuser($u),
'sitename' => $LJ::SITENAME }) . " p?>";
return;
}
if ($u->{'statusvis'} eq "D") {
$title = $ML{'error.deleted.title'};
$body = "<?h1 $ML{'error.deleted.name'} h1?><?p " .
BML::ml('error.deleted.text', { 'user' => LJ::ljuser($u) }) .
" p?>";
return;
}
if ($u->{'statusvis'} eq "X") {
$title = $ML{'error.purged.title'};
$body = "<?h1 $ML{'error.purged.name'} h1?><?p " .
BML::ml('error.purged.text', { 'user' => LJ::ljuser($u) }) .
" p?>";
return;
}
}
# redirect renamed users
if ($u->{'journaltype'} eq "R") {
LJ::load_user_props($u, "renamedto");
return BML::redirect("$LJ::SITEROOT/allpics.bml?user=$u->{'renamedto'}")
if $u->{'renamedto'};
}
my ($can_manage, $getextra);
if ($remote) {
$can_manage = LJ::can_manage($remote, $u->{'userid'});
$getextra = $can_manage && $remote->{'user'} ne $u->{'user'} ? "?authas=$u->{'user'}" : '';
}
#### show pictures
my $info = LJ::get_userpic_info($u, {'load_comments' => 1, 'load_urls' => 1});
my %keywords = ();
while (my ($kw, $pic) = each %{$info->{'kw'}}) {
LJ::text_out(\$kw);
push @{$keywords{$pic->{'picid'}}}, $kw;
}
my %comments = ();
while (my ($pic, $comment) = each %{$info->{'comment'}}) {
LJ::text_out(\$comment);
$comments{$pic} = $comment;
}
my $piccount = 0;
my @pics;
my $defaultpicid = $u ? $u->{'defaultpicid'} : undef;
push @pics, $info->{'pic'}->{$u->{'defaultpicid'}} if $defaultpicid;
push @pics, map { $info->{'pic'}->{$_} } sort { $a <=> $b }
grep { $_ != $defaultpicid && ($info->{'pic'}->{$_}->{'state'} eq 'N' || $can_manage) &&
$info->{'pic'}->{$_}->{'state'} ne 'X' }
keys %{$info->{'pic'}};
foreach my $pic (@pics) {
if ($piccount++ == 0) {
$body .= "<?h1 $ML{'.current'} h1?><?p ";
$body .= BML::ml('.pics', {'user'=> LJ::ljuser($u)});
if ($can_manage) {
$body .= ' ' . BML::ml('.edit2',
{ 'editlink' => "$LJ::SITEROOT/editpics.bml$getextra",
'uploadlink' => "$LJ::SITEROOT/editpics.bml$getextra#upload" });
}
$body .= " p?><div style='margin-left: 50px;'><table cellpadding='5' border='0' cellspacing='1'>";
}
### Keywords
my $eh_keywords = join(", ", sort { lc($a) cmp lc($b) } @{$keywords{$pic->{'picid'}}||[]});
$eh_keywords = LJ::ehtml($eh_keywords);
if ($piccount % 2 == 1) {
$body .= "<tr valign='middle'>";
}
$body .= "<td align='center'>";
my ($apre, $apost);
if ($pic->{'url'}) {
$apre = "<a href='" . LJ::ehtml($pic->{'url'}) . "'>";
$apost = "</a>";
}
$body .= "$apre<img src='$LJ::USERPIC_ROOT/$pic->{'picid'}/$u->{'userid'}' ";
$body .= "width='$pic->{'width'}' height='$pic->{'height'}' alt='' border='0'/>$apost</td><td>";
if ($u->{'defaultpicid'} == $pic->{'picid'}) {
$body .= "$ML{'.default'}<br />";
}
if ($can_manage && $pic->{'state'} eq 'I') {
$body .= "<i>[$ML{'userpic.inactive'}]</i>&nbsp;" . LJ::help_icon('userpic_inactive') . "<br />";
}
if ($eh_keywords) {
$body .= "<b>$ML{'.keywords'}</b> $eh_keywords<br />";
}
# Comments
my $eh_comments = $comments{$pic->{'picid'}};
if ($eh_comments) {
LJ::CleanHTML::clean(\$eh_comments, {
'wordlength' => 40,
'addbreaks' => 0,
'tablecheck' => 1,
'mode' => 'deny',
});
$body .= "$eh_comments\n";
}
$body .= "</td>";
if ($piccount % 2 == 1) {
$body .= "<td width='50px'></td>";
} else {
$body .= "</tr>\n";
}
}
if ($piccount) {
if ($piccount % 2 == 1) {
# finish off this row.
# we need 2 columns: the pic and the text.
$body .= "<td colspan='2'></td></tr>";
}
$body .= "</table></div>";
} else {
if ($can_manage) {
$body = "<?h1 $ML{'.nopics.title'} h1?><?p ";
$body .= BML::ml('.nopics.text2', { 'link' => "$LJ::SITEROOT/editpics.bml$getextra#upload" }) . " p?>";
} else {
$body = "<?h1 $ML{'.nopics.title'} h1?><?p $ML{'.nopics.text.other'} p?>";
}
}
return;
}
_code?><?page
title=><?_code return $title; _code?>
head=><?_code return $head; _code?>
body=><?_code return $body; _code?>
page?><?_c <LJDEP>
link: htdocs/editpics.bml
</LJDEP> _c?>

112
livejournal/htdocs/approve.bml Executable file
View File

@@ -0,0 +1,112 @@
<?_info
nocache=>1
_info?><?page
title=><?_ml .title _ml?>
body<=
<?_code
{
use strict;
my $qs = BML::get_query_string();
return LJ::bad_input($ML{'.error.invalidargument'})
unless $qs && $qs =~ /^(\d+)\.(.+)$/;
my ($aaid, $auth) = ($1, $2);
my $aa = LJ::is_valid_authaction($aaid, $auth);
return LJ::bad_input($ML{'.error.invalidargument'})
unless $aa;
return LJ::bad_input($ML{'.error.actionperformed'})
if $aa->{'used'} eq 'Y';
my $arg = {};
LJ::decode_url_string($aa->{'arg1'}, $arg);
### perform actions according to the action type
# invite users to communities
if ($aa->{'action'} eq 'comm_invite') {
my $dbh = LJ::get_db_writer();
my $targetid = $arg->{'targetid'};
return LJ::bad_input($ML{'.error.internerr.invalidaction'}) unless $targetid;
# add to community
if ($arg->{'member'}) {
LJ::add_friend($aa->{userid}, $targetid);
}
# set up rels with this community
my @rels = ();
push @rels, 'A' if $arg->{'admin'};
push @rels, 'P' if $arg->{'post'};
push @rels, 'M' if $arg->{'moderate'};
push @rels, 'N' if $arg->{'preapprove'};
if (@rels) {
LJ::set_rel_multi( map { [$aa->{userid}, $targetid, $_] } @rels );
}
# mark this authaction as used
$dbh->do("UPDATE authactions SET used='Y' WHERE aaid=?", undef, $aa->{'aaid'});
# return success
my $username = LJ::get_username($aa->{'userid'});
return "<?h1 $ML{'.comm.success'} h1?>".
'<?p '.BML::ml('.comm.text',
{'comm'=>LJ::ljuser($username, { 'type' => 'C' }),
'aopts'=>'href="'.$LJ::SITEROOT.'/friends/add.bml?user='.$username.'"'}).
' p?>';
}
# invite users to shared journals
if ($aa->{'action'} eq 'shared_invite') {
my $dbh = LJ::get_db_writer();
my $targetid = $arg->{'targetid'};
return LJ::bad_input($ML{'.error.internerr.invalidaction'}) unless $targetid;
LJ::set_rel($aa->{'userid'}, $targetid, 'P');
# mark this authaction as used
$dbh->do("UPDATE authactions SET used='Y' WHERE aaid=?", undef, $aa->{'aaid'});
my $username = LJ::get_username($aa->{'userid'});
return "<?h1 $ML{'.shared.success'} h1?>".
'<?p '.BML::ml('.shared.text',
{'shared'=>LJ::ljuser($username, { 'type' => 'S' }),
'aopts'=>'href="'.$LJ::SITEROOT.'/friends/add.bml?user='.$username.'"'}).
' p?>';
}
# approve someone joining a community
if ($aa->{action} eq 'comm_join_request') {
my $dbh = LJ::get_db_writer();
# get user we're adding
my $targetid = $arg->{targetid};
return LJ::bad_input($ML{'.error.internerr.invalidaction'}) unless $targetid;
# add to community
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.approving'} p?>"
unless LJ::approve_pending_member($aa->{userid}, $targetid);
# return success
my $commname = LJ::get_username($aa->{userid});
my $username = LJ::get_username($targetid);
return "<?h1 $ML{'.comm.success'} h1?>".
'<?p ' . BML::ml('.commjoin.text', {
user => LJ::ljuser($username, { type => 'P' }),
comm => LJ::ljuser($commname, { type => 'C' }),
aopts => "href=\"$LJ::SITEROOT/community/members.bml?comm=$commname\"",
}) . ' p?>';
}
# not other action types right now
return LJ::bad_input($ML{'.error.unknownactiontype'});
}
_code?>
<=body
page?>

View File

@@ -0,0 +1,92 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
{
use strict;
use vars qw(%GET);
LJ::set_active_crumb('birthdays');
my $remote = LJ::get_remote();
return "<?needlogin?>" unless $remote;
my $u;
if ($GET{user}) {
$u = LJ::load_user($GET{user});
return BML::ml('.error.invaliduser', { user => LJ::ehtml($GET{user}) })
unless $u;
return BML::ml('.error.badstatus', { user => LJ::ljuser($u->{user}) })
if $u->{statusvis} ne 'V';
}
my $body;
my $lastmon = 0;
$body .= "<?p $ML{'.findothers'} p?>";
$body .= "<form method='get' action='$LJ::SITEROOT/birthdays.bml'>";
$body .= LJ::html_text({ name => 'user', maxlength => 15, size => 15 });
$body .= LJ::html_submit(undef, $ML{'.view'});
$body .= "</form>";
$body .= '<?p ';
if ($u) {
$body .= BML::ml('.description.others', { user => LJ::ljuser($u) });
} else {
$body .= $ML{'.description'};
}
$body .= ' p?>';
# TAG:fr:bml_birthdays:get_bdays
my $dbr = LJ::get_db_reader();
my $sth = $dbr->prepare(
qq|SELECT u.user, u.name, MONTH(bdate) AS 'month', DAYOFMONTH(bdate) AS 'day'
FROM friends f, user u
WHERE f.userid = ?
AND f.friendid = u.userid
AND u.journaltype = 'P'
AND u.statusvis = 'V'
AND u.allow_infoshow = 'Y'
AND MONTH(bdate) != 0
AND DAYOFMONTH(bdate) != 0
LIMIT 750|);
$sth->execute($u ? $u->{userid} : $remote->{userid});
my @bdays;
push @bdays, $_ while $_ = $sth->fetchrow_hashref;
@bdays = sort {
($a->{'month'} <=> $b->{'month'}) ||
($a->{'day'} <=> $b->{'day'}) ||
($a->{'user'} cmp $b->{'user'})
} @bdays;
foreach my $bday (@bdays) {
LJ::text_out(\$bday->{'name'});
if ($bday->{'month'} != $lastmon) {
if ($lastmon) { $body .= "</ul>\n"; }
$lastmon = $bday->{'month'};
$body .= "<?h1 " . LJ::Lang::month_long($lastmon) . " h1?><ul>\n";
}
my $day = sprintf("%2s", $bday->{'day'});
$day =~ s/ /&nbsp;/;
my $name = LJ::ehtml($bday->{'name'});
$body .= "<b><tt>$day</tt></b>: " . LJ::ljuser($bday->{'user'}) . " - $name<br />\n";
}
if (@bdays) {
$body .= "</ul>\n";
} else {
$body .= "<?p $ML{'.nobirthdays'} p?>";
}
return $body;
}
_code?>
<=body
page?><?_c <LJDEP>
link: htdocs/login.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,35 @@
<?_code
{
my ($capid, $anum) = ($GET{capid}, $GET{anum});
($capid, $anum) = LJ::Captcha::session($GET{chal}, 'audio', $GET{cid}) if $GET{chal};
return "Error" unless $capid && $anum;
# determine if we are talking to someone interested in reproxying for us
my $can_reproxy = 0;
unless ($LJ::REPROXY_DISABLE{captchas}) {
my $hdr = BML::get_client_header('X-Proxy-Capabilities');
$can_reproxy = $hdr && $hdr =~ m{\breproxy-file\b}i;
}
my $wav;
eval { $wav = LJ::Captcha::get_audio_data($capid, $anum, $can_reproxy); };
print STDERR "$can_reproxy\n";
if (ref $wav eq 'ARRAY') {
return "Error: unable to handle array without reproxy\n" unless $can_reproxy;
return "Error: captcha not available (no paths)\n" unless scalar @$wav;
if ($wav->[0] =~ m!http://!) {
Apache->request->header_out('X-REPROXY-URL', join(' ', @$wav));
} else {
Apache->request->header_out('X-REPROXY-FILE', $wav->[0]);
}
BML::set_content_type("audio/x-wav");
} elsif ($wav) {
BML::set_content_type("audio/x-wav");
return BML::noparse($wav);
} else {
return "Error: $@";
}
}
_code?>

View File

@@ -0,0 +1,34 @@
<?_code
{
my ($capid, $anum) = ($GET{capid}, $GET{anum});
($capid, $anum) = LJ::Captcha::session($GET{chal}, 'image', $GET{cid}) if $GET{chal};
return "Error" unless $capid && $anum;
# determine if we are talking to someone interested in reproxying for us
my $can_reproxy = 0;
unless ($LJ::REPROXY_DISABLE{captchas}) {
my $hdr = BML::get_client_header('X-Proxy-Capabilities');
$can_reproxy = $hdr && $hdr =~ m{\breproxy-file\b}i;
}
my $png;
eval { $png = LJ::Captcha::get_visual_data($capid, $anum, $can_reproxy); };
if (ref $png eq 'ARRAY') {
return "Error: unable to handle array without reproxy\n" unless $can_reproxy;
return "Error: captcha not available (no paths)\n" unless scalar @$png;
if ($png->[0] =~ m!http://!) {
Apache->request->header_out('X-REPROXY-URL', join(' ', @$png));
} else {
Apache->request->header_out('X-REPROXY-FILE', $png->[0]);
}
BML::set_content_type("image/png");
} elsif ($png) {
BML::set_content_type("image/png");
return BML::noparse($png);
} else {
return "Error: $@";
}
}
_code?>

View File

@@ -0,0 +1,165 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
use strict;
my $body;
if ($LJ::SERVER_DOWN) {
$body = LJ::server_down_html();
return $body;
}
if ($LJ::USE_SSL && ! $LJ::IS_SSL && $FORM{'ssl'} ne "no") {
return BML::redirect("$LJ::SSLROOT/changepassword.bml");
}
my $crumb = $LJ::IS_SSL ? 'securechangepass' : 'changepass';
LJ::set_active_crumb($crumb);
my $update_form = sub {
my $ret;
# else, show the form to change:
$ret .= "<form action='changepassword.bml' method='post'>\n";
$ret .= LJ::html_hidden(mode => 'submit',
ssl => $GET{'ssl'});
$ret .= "<?h1 $ML{'.changepassword.header'} h1?>\n";
$ret .= "<?p $ML{'.changepassword.instructions'} p?>\n";
my $remote = LJ::get_remote();
# Warn them if logged in and not validated
if (!LJ::did_post() && $remote && $remote->{'status'} ne 'A') {
$ret .= "<?warningbar <b>$ML{'label.warning'}</b> $ML{'.error.notvalidated'} warningbar?>";
$ret .= "<br />";
}
$ret .= "<?standout\n";
$ret .= "$ML{'Username'}:<br />\n";
# we make the field for the new password *longer* than the max length
# for a password - that way we can tell if someone is trying to use an
# excessively long password, instead of silently truncating it.
my $hval = LJ::ehtml($remote ? $remote->{'user'} : $POST{'user'});
$ret .= "<input name='user' size='30' maxlength='15' value='$hval' /><br />\n";
$ret .= "$ML{'.oldpassword'}<br />\n";
$ret .= "<input type='password' name='password' size='30' maxlength='30' /><br />\n";
$ret .= "$ML{'.newpassword'}<br />\n";
$ret .= "<input type='password' name='newpass1' size='30' maxlength='31' /><br />\n";
$ret .= "$ML{'.newpasswordagain'}<br />\n";
$ret .= "<input type='password' name='newpass2' size='30' maxlength='31' /><br />\n";
$ret .= "standout?>\n";
$ret .= "<?h1 $ML{'Proceed'} h1?>\n";
$ret .= "<?p $ML{'.proceed.instructions'} p?>\n";
$ret .= "<?standout\n";
$ret .= "<input type='submit' value='$ML{'.btn.proceed'}' />\n";
$ret .= "standout?>\n";
$ret .= "</form>\n";
return $ret;
};
unless (LJ::did_post()) {
$body .= $update_form->();
} elsif ($POST{'mode'} eq 'submit') {
my $user = LJ::canonical_username($POST{'user'});
my $password = $POST{'password'};
my $newpass1 = LJ::trim($POST{'newpass1'});
my $newpass2 = LJ::trim($POST{'newpass2'});
my $remote = LJ::get_remote();
my $u = LJ::load_user($user);
my @errors = ();
if ($user eq "test") { push @errors, $ML{'.error.changetestaccount'}; }
unless ($user) {
push @errors, $ML{'.error.mustenterusername'};
} else {
unless (defined $u) {
push @errors, BML::ml('.error.invaliduser', {'user' => $user} );
} else {
if (LJ::login_ip_banned($u)) {
push @errors, $ML{'error.ipbanned'};
} elsif ($u->{'password'} eq "" || $u->{'password'} ne $password) {
push @errors, $ML{'.error.badoldpassword'};
LJ::handle_bad_login($u);
}
}
}
if ($newpass1 ne $newpass2) {
push @errors, $ML{'.error.badnewpassword'};
} else {
if ($newpass1 eq "") {
push @errors, $ML{'.error.blankpassword'};
} elsif (length $newpass1 > 30) {
push @errors, $ML{'.error.characterlimit'};
} else {
my @checkpass = LJ::run_hooks("bad_password",
{ 'user' => $u->{'user'}, 'password' => $newpass1,
'name' => $u->{'name'}, 'email' => $u->{'email'} });
if (@checkpass && $checkpass[0]->[0]) {
push @errors, BML::ml('.error.badcheck', {'error' => $checkpass[0]->[0]});
}
}
}
# don't allow changes if email address is not validated
unless ($u->{'status'} eq 'A') {
push @errors, $ML{'.error.notvalidated'};
}
unless (LJ::is_ascii($newpass1)) {
push @errors, $ML{'.error.nonascii'};
}
if (@errors) {
$body .= LJ::error_list(@errors);
$body .= $update_form->();
return $body;
}
## make note of changed password
my $dbh = LJ::get_db_writer();
my $oldval = Digest::MD5::md5_hex($u->{'password'} . "change");
LJ::infohistory_add($u, 'password', $oldval);
LJ::update_user($u, { password => $POST{'newpass1'} });
# Kill all sessions, forcing user to relogin
$u->kill_all_sessions;
LJ::send_mail({
'to' => $u->{'email'},
'from' => $LJ::ADMIN_EMAIL,
'fromname' => $LJ::SITENAME,
'charset' => 'utf-8',
'subject' => $ML{'.email.subject'},
'body' => BML::ml('.email.body', {'sitename'=>$LJ::SITENAME, 'siteroot'=>$LJ::SITEROOT})});
$body = "<?h1 $ML{'Success'} h1?><?p $ML{'.success.text'} p?>";
# if they were logged in, tell them to relogin
$body .= "<?p " . BML::ml('.relogin', { 'aopts' => "href='/login.bml'" }) . " p?>" if $remote;
LJ::run_hooks("post_changepassword", {
"u" => $u,
"newpassword" => $POST{'newpass1'},
"oldpassword" => $u->{'password'},
});
}
return $body;
_code?>
<=body
page?><?_c <LJDEP>
post: htdocs/changepassword.bml
lib: Digest::MD5
hook: post_changepassword
</LJDEP> _c?>

View File

@@ -0,0 +1,269 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
{
use strict;
use vars qw(%GET %POST);
LJ::set_active_crumb('createcommunity');
return LJ::server_down_html() if $LJ::SERVER_DOWN;
return "<?badinput?>" unless LJ::text_in(\%POST);
my $mode = $POST{mode} || 'getinfo';
my $remote = LJ::get_remote();
return "<?needlogin?>" if !$remote;
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.notperson'} p?>"
if $remote->{journaltype} ne 'P';
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.notactive'} p?>"
unless $remote->{statusvis} eq 'V';
if ($remote->underage) {
return BML::redirect("$LJ::SITEROOT/agecheck/?s=1");
}
# fix up the incoming data (is used in getinfo mode and submit mode so it's here)
$POST{membership} = 'open'
unless $POST{membership} =~ m/^(?:open|moderated|closed)$/;
$POST{postlevel} = 'members'
unless $POST{postlevel} =~ m/^(?:members|select)$/;
$POST{nonmember_posting} = '0'
unless $POST{nonmember_posting} =~ m/^[01]$/;
$POST{moderated} = '0'
unless $POST{moderated} =~ m/^[01]$/;
# MODE: submit - try to create an account. might change mode
# if there are errors, we'll populate $error and
# return to "getinfo" mode below
my $error;
SUBMIT:
while ($mode eq 'submit') # using while instead of if so we can 'last' out of it
{
return "<b>$ML{'Error'}</b>: $ML{'.error.postrequired'}" unless LJ::did_post();
my $user = LJ::canonical_username($POST{user});
my $title = $POST{title} || $user;
# reject this email?
return LJ::sysban_block(0, "Create user blocked based on email",
{ new_user => $user, email => $remote->{email}, name => $user })
if LJ::sysban_check('email', $remote->{email});
$error = "$ML{'error.usernamelong'}" if length($user) > 15;
$error = "$ML{'error.usernameinvalid'}" if $POST{user} && !$user;
$error = "$ML{'.error.username.mustenter'}" unless $POST{user};
foreach my $re ("^system\$", @LJ::PROTECTED_USERNAMES) {
next unless $user =~ /$re/;
# you can give people sharedjournal priv ahead of time to create
# reserved communities:
next if LJ::check_priv($remote, "sharedjournal", $user);
$error = "$ML{'.error.username.reserved'}";
}
my $u = LJ::load_user($user);
my $second_submit = 0;
if ($u) {
my $in_use = 1;
if ($u->{email} eq $remote->{email}) {
if (LJ::login_ip_banned($u)) {
# brute-force possible going on
} else {
if ($u->{password} eq $remote->{password}) {
# oh, they double-clicked the submit button
$second_submit = 1;
# if we found a comm and everything matches, they double hit. if
# we found a person/etc, then they tried to recreate their community,
# which isn't allowed anymore
$in_use = $u->{journaltype} eq 'C' ? 0 : 1;
} else {
LJ::handle_bad_login($u);
}
}
}
if ($in_use) {
$error = "$ML{'.error.username.inuse'}";
}
}
last SUBMIT if $error;
my $qclusterid = LJ::new_account_cluster() + 0;
die "Cluster 0 not supported" unless $qclusterid;
my $userid = ref $u ? $u->{userid} : 0;
unless ($second_submit) {
my $dbh = LJ::get_db_writer();
$dbh->do("INSERT INTO user (user, email, status, caps, name, clusterid, dversion, journaltype) ".
"VALUES (?, ?, ?, ?, ?, ?, $LJ::MAX_DVERSION, 'C')",
undef, $user, $remote->{email}, $remote->{status}, int($LJ::NEWUSER_CAPS), $title, $qclusterid);
if ($dbh->err) {
return "<?h1 $ML{'Error'} h1?><?p $ML{'error.procrequest'} <b>" . $dbh->errstr . "</b> p?>";
}
$userid = $dbh->{'mysql_insertid'};
$dbh->do("REPLACE INTO useridmap (userid, user) VALUES (?, ?)", undef, $userid, $user);
$dbh->do("REPLACE INTO userusage (userid, timecreate) VALUES (?, NOW())", undef, $userid);
# set any properties that get set in new users
$u = LJ::load_userid($userid);
while (my ($name, $val) = each %LJ::USERPROP_INIT) {
LJ::set_userprop($u, $name, $val);
}
# since they're a community, let's do more setup
$dbh->do("REPLACE INTO community (userid, membership, postlevel) VALUES (?, ?, ?)",
undef, $userid, $POST{membership}, $POST{postlevel});
LJ::set_userprop($u, 'nonmember_posting', $POST{nonmember_posting} + 0);
LJ::set_userprop($u, 'moderated', $POST{moderated} + 0);
LJ::set_rel($userid, $remote->{userid}, 'M') if $POST{moderated}; # moderator if moderated
LJ::set_rel($userid, $remote->{userid}, 'A'); # maintainer
LJ::join_community($remote, $u, 0, 1); # make them a member of the community
LJ::run_hooks("post_create", {
'userid' => $userid,
'user' => $user,
});
}
my $nu = LJ::load_userid($userid, "force");
# log creation
$nu->log_event('account_create', { remote => $remote });
# local sites may want to override what happens at this point
my $ret;
my $redirect;
my $stop_output;
LJ::run_hooks("create.bml_postsession", {
post => \%POST,
u => $nu,
type => 'community',
redirect => \$redirect,
ret => \$ret,
stop_output => \$stop_output,
});
return BML::redirect($redirect) if $redirect;
return $ret if $stop_output;
$ret = "<?h1 $ML{'.success.head'} h1?><?p $ML{'.success.text1'} p?>";
my $uri = LJ::journal_base($nu);
$ret .= "<?p $ML{'.success.text2'} p?>\n";
$ret .= "<?standout <font size='+1' face='arial'><b><a href='$uri'>$uri/</a></b></font> standout?>\n";
$ret .= "<?p $ML{'.success.text3'} p?>\n";
$ret .= "<form method='get' action='$LJ::SITEROOT/editinfo.bml?authas=$nu->{user}'>";
$ret .= "<p align='center'>" . LJ::html_submit(undef, "$ML{'.success.btn.enterinfo'} &rarr;") . "</p>";
$ret .= "</form>\n";
return $ret;
}
if ($mode eq "getinfo" || $error)
{
my $ret;
if ($error) {
$ret .= "<?errorbar <strong>$ML{'.errors.label'}</strong><ul>";
$ret .= "<li>$error</li>";
$ret .= "</ul> errorbar?>";
}
$ret .= "<?p $ML{'.create.text'} p?>" unless $error;
$ret .= "<form action=\"create.bml\" method=\"post\">\n";
$ret .= LJ::html_hidden(mode => 'submit', ssl => $FORM{'ssl'});
$ret .= "<ol>";
# username
my $v = LJ::ehtml($FORM{'user'});
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.username.head'}</div>";
$ret .= "<p class='formitemFlag'>$error</p>";
$ret .= "<div class='formitemDesc'>" . BML::ml(".username.text", { sitename => $LJ::SITENAME }) . "</div>";
$ret .= LJ::html_text({'name' => 'user', 'size' => 15, 'maxlength' => 15, 'value' => $v, raw => 'style="<?commloginboxstyle?>"' });
$ret .= "<br />" . BML::ml('.person', { aopts => "href='$LJ::SITEROOT/create.bml'" });
$ret .= "<div class='formitemNote'>$ML{'.username.charsallowed'}</div>" unless $error;
$ret .= "</div></li>";
# account title
$v = LJ::ehtml($FORM{'title'});
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.name.head'}</div>";
$ret .= "<div class='formitemDesc'>$ML{'.name.text'}</div>";
$ret .= LJ::html_text({ name => 'title', style => 'width: 60%;', maxlength => 80, value => $v, });
$ret .= "</div></li>";
# membership levels
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'/community/settings.bml.label.membership'}" .
"</div><div class='formitemDesc'>$ML{'/community/settings.bml.label.whocanjoin'}</div><div><p>";
$ret .= LJ::html_check({ type => 'radio', name => 'membership', id => 'memopen',
value => 'open', selected => ($POST{membership} eq 'open' ? 1 : 0)});
$ret .= "<label for='memopen' $ML{'/community/settings.bml.label.openmemb'}</label><br /></p><p>";
$ret .= LJ::html_check({ type => 'radio', name => 'membership', id => 'memmoderated',
value => 'moderated', selected => ($POST{membership} eq 'moderated' ? 1 : 0)});
$ret .= "<label for='memmoderated' $ML{'/community/settings.bml.label.moderatedmemb'}</label></p><p>";
$ret .= LJ::html_check({ type => 'radio', name => 'membership', id => 'memclosed',
value => 'closed', selected => ($POST{membership} eq 'closed' ? 1 : 0)});
$ret .= "<label for='memclosed' $ML{'/community/settings.bml.label.closedmemb2'}</label></p>";
$ret .= "</div></div></li>";
# posting access options
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'/community/settings.bml.label.postaccess'}" .
"</div><div class='formitemDesc'>$ML{'/community/settings.bml.label.whocanpost'}</div><div><p>";
$ret .= LJ::html_check({ type => 'radio', name => 'postlevel', id => 'postopen',
value => 'members', selected => ($POST{postlevel} eq 'members' ? 1 : 0)});
$ret .= "<label for='postopen'>$ML{'/community/settings.bml.label.anybodycan'}</label></p><p>";
$ret .= LJ::html_check({ type => 'radio', name => 'postlevel', id => 'postclosed',
value => 'select', selected => ($POST{postlevel} eq 'select' ? 1 : 0)});
$ret .= "<label for='postclosed'>$ML{'/community/settings.bml.label.selcan'}</label></p>";
$ret .= "</div></div></li>";
# nonmember posting options
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'/community/settings.bml.label.nmheader'}" .
"</div><div class='formitemDesc'>$ML{'/community/settings.bml.label.nmtext'}</div><div><p>";
$ret .= LJ::html_check({ type => 'radio', name => 'nonmember_posting', id => 'nonopen',
value => '0', selected => ($POST{nonmember_posting} eq '0' ? 1 : 0)});
$ret .= "<label for='nonopen'>$ML{'/community/settings.bml.label.nmcant'}</label></p><p>";
$ret .= LJ::html_check({ type => 'radio', name => 'nonmember_posting', id => 'nonclosed',
value => '1', selected => ($POST{nonmember_posting} eq '1' ? 1 : 0)});
$ret .= "<label for='nonclosed'>$ML{'/community/settings.bml.label.nmcan'}</label></p>";
$ret .= "</div></div></li>";
# moderated options
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'/community/settings.bml.label.modheader'}" .
"</div><div class='formitemDesc'>$ML{'/community/settings.bml.label.modtext'}</div><div><p>";
$ret .= LJ::html_check({ type => 'radio', name => 'moderated', id => 'radunmod',
value => '0', selected => ($POST{moderated} eq '0' ? 1 : 0)});
$ret .= "<label for='radunmod'>$ML{'/community/settings.bml.label.modisnt'}</label></p><p>";
$ret .= LJ::html_check({ type => 'radio', name => 'moderated', id => 'radmod',
value => '1', selected => ($POST{moderated} eq '1' ? 1 : 0)});
$ret .= "<label for='radmod'>$ML{'/community/settings.bml.label.modis'}</label></p>";
$ret .= "</div></div></li>";
LJ::run_hooks("create.bml_opts", {
post => \%POST,
get => \%GET,
ret => \$ret,
});
$ret .= "</ol>";
$ret .= "<div style='width:600; text-align: center'>";
$ret .= "<input type='submit' value=\"$ML{'.btn.create'}\">";
$ret .= "</div>";
$ret .= "</form>";
return $ret;
}
return "$ML{'error.unknownmode'}: <b>$mode</b>";
}
_code?>
<=body
page?>

View File

@@ -0,0 +1,12 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
LJ::set_active_crumb('community');
return $ML{'.main'};
_code?>
<=body
page?><?_c <LJDEP>
# NONE
</LJDEP> _c?>

View File

@@ -0,0 +1,134 @@
<?_code
LJ::set_active_crumb('joincomm');
$title = $ML{'.title'};
$body = "";
# is there a user out there?
my $remote = LJ::get_remote();
unless ($remote) {
$body = "<?h1 $ML{'Sorry'}.. h1?><?p $ML{'.label.loginfirst'} p?>";
return;
}
# bad statusvis?
unless ($remote->{statusvis} eq 'V') {
$body = "<?h1 $ML{'.error.statusvis.title'} h1?><?p $ML{'.error.statusvis.body'} p?>";
return;
}
# get info about the community
my $cuserid = $FORM{'cuserid'}+0;
my $cu = $FORM{comm} ?
LJ::load_user($FORM{comm}) : # they gave us the comm name
LJ::load_userid($cuserid); # they gave us the comm id
# NOTE: we wrapped this in an eval due to code going live; the library isn't going to go
# live at the same time as the BML file, and we don't want weird things happening, so we
# verify that this is all good and return an error if it's not okay.
my $ci;
eval { $ci = LJ::get_community_row($cu); };
if ($@) {
$body = "<?h1 Temporarily Disabled h1?><?p This page is disabled while we update the site. Please try again later. p?>";
return;
}
$cuserid = $ci->{'userid'};
LJ::text_out(\$ci->{'name'});
my $ecname = LJ::ehtml($ci->{'name'});
# does this community even exit?
unless ($cu) {
$body .= "<?h1 $ML{'Error'} h1?><?p $ML{'.label.errorcomminfo'} p?>";
return;
}
# make sure a community doesn't join a community (that's confusing
# or something)
unless ($remote->{'journaltype'} eq "P") {
$body .= "<?h1 $ML{'Error'} h1?><?p $ML{'.label.commlogged'} p?>";
return;
}
# ensure this user isn't banned
if (LJ::is_banned($remote, $cuserid)) {
$body .= "<?h1 $ML{'Sorry'} h1?><?p $ML{'.label.banned'} p?>";
return;
}
# and make sure they're not already a member
if (LJ::is_friend($cuserid, $remote->{userid})) {
$body .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.already.member'} p?>";
return;
}
# get the list of maintainers and their usernames
my $dbr = LJ::get_db_reader();
my $admins = $dbr->selectcol_arrayref("SELECT u.user FROM useridmap u, reluser r ".
"WHERE r.userid=$cuserid AND r.targetid=u.userid AND r.type='A'") || [];
my $list = "<ul>";
foreach (sort @$admins) { $list .= "<li><?ljuser $_ ljuser?></li>"};
$list .= "</ul>";
# can't join closed communities
if ($ci->{membership} eq 'closed') {
$body .= "<?h1 $ML{'Sorry'} h1?><?p " .
BML::ml('.error.closed', { admins => $list }) .
" p?>";
return;
}
# now do the join
if ($POST{confirm}) {
# can members join this community openly?
if ($ci->{membership} ne 'open') {
# hit up the maintainers to let them know a join was requested
LJ::comm_join_request($cu, $remote);
$body .= "<?h1 $ML{'.reqsubmitted.title'} h1?><?p $ML{'.reqsubmitted.body'} $list p?>";
return;
}
# make remote user a friend of the community
LJ::join_community($remote, $cu, $FORM{addfriend});
# success message
$body .= "<?h1 $ML{'.success'} h1?><?p " . BML::ml('.label.membernow',
{ username => $ci->{user}, commname => $ecname}) . " p?>";
# if community permits it, tell the user they have access
if ($ci->{postlevel} eq "members") {
$body .= "<?p $ML{'.label.allowposting'} p?>";
} else {
$body .= "<?p " . BML::ml('.label.auth', { admins => $list }) . " p?>";
}
} else {
if ($ci->{membership} ne 'open') {
$body .= "<?h1 $ML{'.request.title'} h1?><?p ";
$body .= BML::ml('.request.body', { comm => LJ::ljuser($cu) }) . "<br /> p?>";
$body .= "<div style='margin-left: 30px;'><form method='post' action='join.bml'>";
$body .= "<input type='hidden' name='cuserid' value='$ci->{userid}' />";
$body .= "<input type='hidden' name='confirm' value='1' />";
$body .= "<input type='submit' value=\"$ML{'.button.join'}\" /></form></div>";
return;
}
$body .= "<?h1 $ML{'.label.sure'} h1?><?p " . BML::ml('.label.expls', { maintainer => $ecname });
$body .= "<form method='post' action='join.bml'>";
$body .= "<input type='hidden' name='cuserid' value='$ci->{'userid'}' />";
$body .= "<input type='hidden' name='confirm' value='1' /><center>";
$body .= "<input type='checkbox' name='addfriend' checked>";
$body .= BML::ml('.label.addtofriends', { maintainer => $ecname });
$body .= "<br><input type='submit' value=\"$ML{'.button.join'}\" /></center></form> p?>";
}
return;
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?><?_c <LJDEP>
link: htdocs/login.bml, htdocs/userinfo.bml
post: htdocs/community/join.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,68 @@
<?_code
{
use strict;
use vars qw($title $body);
LJ::set_active_crumb('leavecomm');
$title = $ML{'.title'};
$body = "";
my $error = sub {
$body = "<?h1 $ML{'Error'} h1?><?p $_[0] p?>";
return;
};
my $remote = LJ::get_remote();
return $error->($ML{'.label.logoutfirst'}) unless $remote;
# get info about the community
my $cuserid = $FORM{'cuserid'}+0;
my $cu = $FORM{comm} ?
LJ::load_user($FORM{comm}) : # they gave us the comm name
LJ::load_userid($cuserid); # they gave us the comm id
# error check
return $error->($ML{'.label.infoerror'})
unless $cu && $cu->{journaltype} =~ /[CS]/;
# used in both paths below
my $ecname = LJ::ehtml($cu->{'name'});
if ($FORM{confirm}) {
# get current list of maintainers to make sure the last one doesn't leave
my $maintids = LJ::load_rel_user($cu->{userid}, 'A');
return $error->($ML{'error.nodb'}) unless ref $maintids eq 'ARRAY';
# error if we're a maintainer and there are no others
my $ismaint = grep { $_ == $remote->{userid} } @$maintids;
my $othermaints = grep { $_ != $remote->{userid} } @$maintids;
return $error->($ML{'.label.lastmaintainer'}) if $ismaint && !$othermaints;
# remove user from community's friends list
LJ::leave_community($remote, $cu, $FORM{removefriend});
# success message
$body .= "<?h1 $ML{'.success'} h1?><?p " .
BML::ml('.label.removed', { commuser => $cu->{user}, commname => $ecname }) .
" p?>";
} else {
# show a confirmation form
$body .= "<?h1 $ML{'.sure'} h1?><?p " . BML::ml('.label.buttontoleave', { commname => $ecname }) . " p?>";
$body .= '<form method="post" action="leave.bml">';
$body .= LJ::html_hidden("cuserid", $cu->{userid}, "confirm", 1) . "<p align='center'>";
$body .= LJ::html_check({ name => 'removefriend', selected => 1, value => 1 }) . " ";
$body .= BML::ml('.label.removefromfriends', { user => $ecname });
$body .= '<br /><br />' . LJ::html_submit(undef, $ML{'.button.leave'}) . '</p></form>';
}
return;
}
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?><?_c <LJDEP>
link: htdocs/login.bml, htdocs/userinfo.bml
post: htdocs/community/leave.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,189 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
use strict;
use vars qw(%GET %POST);
return LJ::server_down_html() if ($LJ::SERVER_DOWN);
LJ::set_active_crumb('managecommunity');
my $dbr = LJ::get_db_reader();
my $ret;
my $remote = LJ::get_remote();
unless ($remote) {
return "<?needlogin?>";
}
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.badaccounttype'} p?>"
unless $remote->{journaltype} eq 'P';
$ret .= "<?h1 $ML{'.commlist.header'} h1?><?p $ML{'.commlist.text'} p?>";
$ret .= "<div style='margin: 15px 0 15px 40px'>";
my %show;
# what communities does remote
my $cids = LJ::load_rel_target($remote, 'A');
my %admin;
if ($cids) { foreach (@$cids) { $admin{$_} = $show{$_} = 1; } }
# keep track of what communities remote moderates
my $mods = LJ::load_rel_target($remote, 'M');
my %mods;
my %modcount;
if ($mods) { foreach (@$mods) { $mods{$_} = $show{$_} = 1; } }
my %names = ();
my %pending = ();
if (%show) {
my $in = join(',', keys %show);
my $us = LJ::load_userids(keys %show);
my $sth = $dbr->prepare("SELECT userid, membership FROM community ".
"WHERE userid IN ($in)");
$sth->execute;
my $udbr;
while (my ($uid, $membership) = $sth->fetchrow_array) {
my $cu = $us->{$uid};
next unless $cu && $cu->{statusvis} eq "V";
$names{$uid} = [ $cu->{user}, $cu->{name}, -1 ];
if ($mods{$uid}) {
$udbr = LJ::get_cluster_reader($cu);
my $sql = "SELECT COUNT(*) FROM modlog WHERE journalid=$uid";
$modcount{$uid} = $names{$uid}[2] = $udbr->selectrow_array($sql) || 0;
}
if ($membership eq 'moderated') {
my $ids = LJ::get_pending_members($uid) || [];
$pending{$uid} = scalar @$ids;
}
}
}
# deleted and expunged communities won't show up in this
if (%names) {
# Make up the first table row, linking to the other sort methods
my @snames;
$ret .= "<table cellpadding='5'><tr style='text-align: left;'>";
if (!defined $GET{'sortby'} || $GET{'sortby'} eq 'username') {
$ret .= "<th>$ML{'.commlist.username'}</th>";
@snames = sort { $names{$a}->[0] cmp $names{$b}->[0] } keys %names;
} else {
$ret .= "<th><a href='".BML::self_link({'sortby' => 'username'}) . "'>$ML{'.commlist.username'}</a></th>";
}
if ($GET{'sortby'} eq 'title') {
$ret .= "<th>$ML{'.commlist.title'}</th>";
@snames = sort { $names{$a}->[1] cmp $names{$b}->[1] } keys %names;
} else {
$ret .= "<th><a href='".BML::self_link({'sortby' => 'title'}) . "'>$ML{'.commlist.title'}</a></th>";
}
$ret .= "<th>$ML{'.commlist.actions'}</th>";
if ($GET{'sortby'} eq 'mod') {
$ret .= "<th>$ML{'.commlist.moderation'}</th>";
@snames = sort { $names{$b}->[2] <=> $names{$a}->[2] } keys %names;
} elsif (@$mods) {
$ret .= "<th><a href='".BML::self_link({'sortby' => 'mod'}) . "'>$ML{'.commlist.moderation'}</a></th>";
}
foreach my $id (@snames) {
my $user = $names{$id}->[0];
my $name = $names{$id}->[1];
$ret .= "<tr><td><?ljcomm $user ljcomm?></td><td>" . LJ::eall($name) . "</td><td nowrap='nowrap'>";
if ($admin{$id}) {
$ret .= BML::ml('Actionlink', {
'link'=>"<a href='/editinfo.bml?authas=$user'>$ML{'.commlist.actinfo'}</a>"}) . "&nbsp;&nbsp;";
$ret .= BML::ml('Actionlink', {
'link'=>"<a href='/community/settings.bml?comm=$user'>$ML{'.commlist.actsettings2'}</a>"}) . "&nbsp;&nbsp;";
$ret .= BML::ml('Actionlink', {
'link'=>"<a href='/community/sentinvites.bml?comm=$user'>$ML{'.commlist.actinvites'}</a>"}) . "&nbsp;&nbsp;";
my $pend = $pending{$id} ? BML::ml('.commlist.actpending', { num => $pending{$id},
aopts => "href=\"/community/pending.bml?comm=$user\"" }) : '';
$ret .= BML::ml('Actionlink', {
'link'=>"<a href='/community/members.bml?comm=$user'>$ML{'.commlist.actmembers2'}</a>$pend"}) . "&nbsp;&nbsp;";
}
$ret .= "</td><td align='center'>";
if ($mods{$id}) {
my $num = $modcount{$id} || "0"; # relying on this to be <b>-wrapped
$ret .= BML::ml('.commlist.moderation.num', { num => $num,
link => "/community/moderate.bml?comm=$user" });
}
$ret .= "</td></tr>";
}
$ret .= "</table>";
} else {
$ret .= "$ML{'.commlist.none'}";
}
$ret .= "</div>";
# save any changes
if (LJ::did_post()) {
my $op = $POST{email};
$op = '' if $op && $op !~ /[N]/; # add D back in here when Digest goes live
LJ::set_userprop($remote, 'opt_communityjoinemail', $op);
my $mod_emails = $POST{modemail} ? 1 : 0;
LJ::set_userprop($remote, 'opt_nomodemail', $mod_emails ? undef : 1);
}
# load up our userprop
LJ::load_user_props($remote, 'opt_communityjoinemail', 'opt_nomodemail');
my $op = $remote->{opt_communityjoinemail};
$op ||= 'Y';
my $mod_emails = $remote->{opt_nomodemail} ? 0 : 1;
# form for setting email option
$ret .= "<?h1 $ML{'.joinmail.title'} h1?>";
$ret .= "<?p $ML{'.joinmail.body'} p?>";
$ret .= "<form method='post' action='manage.bml'>";
$ret .= "<div style='margin-left: 30px;'>";
$ret .= LJ::html_check({ type => 'radio', name => 'email', id => 'email_all',
value => '', selected => ($op eq 'Y') });
$ret .= "<label for='email_all'>$ML{'.joinmail.email.all'}</label><br />";
if (0) {
# we don't do this right now, because the maintenance job to send email isn't
# ready for use on LJ. this will be removed and the option enabled when that
# task is rewritten. NOTE: add D up above in did_post() block.
$ret .= LJ::html_check({ type => 'radio', name => 'email', id => 'email_digest',
value => 'D', selected => ($op eq 'D') });
$ret .= "<label for='email_digest'>$ML{'.joinmail.email.digest'}</label><br />";
}
$ret .= LJ::html_check({ type => 'radio', name => 'email', id => 'email_none',
value => 'N', selected => ($op eq 'N') });
$ret .= "<label for='email_none'>$ML{'.joinmail.email.none'}</label><br />";
$ret .= "</div>";
# options for getting moderation emails
$ret .= "<?p $ML{'.modemail.body'} p?>";
$ret .= "<div style='margin-left: 30px;'>";
$ret .= LJ::html_check({ type => 'radio', name => 'modemail', id => 'modemail_yes',
value => '1', selected => $mod_emails });
$ret .= "<label for='modemail_yes'>$ML{'.modemail.yes'}</label><br />";
$ret .= LJ::html_check({ type => 'radio', name => 'modemail', id => 'modemail_no',
value => '0', selected => !$mod_emails });
$ret .= "<label for='modemail_no'>$ML{'.modemail.no'}</label><br />";
$ret .= "</div><?p ";
$ret .= LJ::html_submit($ML{'.joinmail.save'});
$ret .= " p?></form>";
$ret .= "<?h1 $ML{'.create.header'} h1?>";
$ret .= "<?p " . BML::ml('.create.text', {'link'=>'/community/create.bml'}) . " p?>";
return $ret;
_code?>
<=body
page?>

View File

@@ -0,0 +1,455 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
{
use strict;
use vars qw(%GET %POST);
LJ::set_active_crumb('commmembers');
return LJ::server_down_html() if $LJ::SERVER_DOWN;
# always have links at top
my $ret = BML::ml('Backlink', {
'link' => '/community/manage.bml',
'text' => $ML{'.manage2'},
});
# get remote
my $remote = LJ::get_remote();
unless ($remote) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'error.noremote'} p?>";
return $ret;
}
my $cname = $GET{'comm'};
return BML::redirect("$LJ::SITEROOT/community/manage.bml") unless $cname;
# get $c object
my $c = LJ::load_user($cname);
unless ($c) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.nocomm'} p?>";
return $ret;
}
my $cid = $c->{'userid'};
# is $remote an admin?
unless (LJ::can_manage_other($remote, $c)) {
$ret .= "<?h1 $ML{'Error'} h1?><?p ";
$ret .= BML::ml('.error.noaccess',
{ 'comm' => LJ::ljuser($cname, { 'type' => 'C' }) });
$ret .= " p?>";
return $ret;
}
my @allattribs = ('member', 'post', 'preapprove', 'moderate', 'admin');
my %attrshort = ( X => 'member', P => 'post', N => 'preapprove', M => 'moderate', A => 'admin');
my %attrshort_r = ( map { $attrshort{$_} => $_ } keys %attrshort ); # reversed
# saving a form submission
if ($POST{'action:update'}) {
# validate form auth
return "<?h1 $ML{'Error'} h1?><?p $ML{'error.invalidform'} p?>"
unless LJ::check_form_auth();
my @userids = split(',', $POST{'ids'});
my @saveattribs = split(',', $POST{'attribs'});
# now we need to create our 'was' list
my %was; # ( userid => { attrib => 1; } )
my %users; # ( userid => username )
foreach my $row (split ';', $POST{was}) { # UID:UNAME:MNPX;UID:UNAME:MX;UID:UNAME:AM
# if this row matches...
if ($row =~ /^(\d+):(\w+):(\w+)$/) {
my ($uid, $name, $attrs) = ($1, $2, $3);
$uid += 0;
next unless $uid && $name && $attrs;
# split attrs and setup
$was{$uid}->{$attrshort{$_}} = 1 foreach split '', $attrs;
$users{$uid} = $name;
}
}
# invite new users
my @to_add;
my @add_errors;
foreach my $num (1..5) {
$POST{"add_$num"} = LJ::trim($POST{"add_$num"});
next unless $POST{"add_$num"} =~ /^\w+$/;
my $target = LJ::load_user($POST{"add_$num"});
unless ($target) {
push @add_errors, BML::ml('.error.nouser',
{ 'user' => $POST{"add_$num"} });
next;
}
unless ($target->{statusvis} eq 'V') {
push @add_errors, BML::ml('.error.notactive',
{ 'user' => LJ::ljuser($POST{"add_$num"}) });
next;
}
my @attr = grep { defined $POST{"add_${num}_$_"} } @saveattribs;
unless (@attr) {
push @add_errors, BML::ml('.error.noattr',
{ 'user' => LJ::ljuser($POST{"add_$num"},
{ 'type' => $target->{'journaltype'} }) });
next;
}
unless ($target->{'journaltype'} eq 'P') {
push @add_errors, BML::ml('.error.invaliduser',
{ 'user' => LJ::ljuser($POST{"add_$num"},
{ 'type' => $target->{'journaltype'} }) });
next;
}
if (grep { $target->{'userid'} == $_ } @userids) {
push @add_errors, BML::ml('.error.alreadyadded',
{ 'user' => LJ::ljuser($POST{"add_$num"},
{ 'type' => $target->{'journaltype'} }) });
next;
}
# insert authactions row
push @to_add, [ $target, \@attr ];
}
return LJ::bad_input(@add_errors) if @add_errors;
# now do the additions if any were needed
my @fail;
my @invited;
if (@to_add) {
foreach my $row (@to_add) {
# good, let's extend an invite to this person
my ($target, $attrs) = @$row;
if (LJ::send_comm_invite($target, $c, $remote, $attrs)) {
push @invited, $row;
} else {
push @fail, [ $target, LJ::last_error_code() ];
}
}
}
if (@fail) {
my @bad;
foreach (@fail) {
if ($_->[1] eq 'comm_user_has_banned') {
push @bad, BML::ml('.error.adding', { user => LJ::ljuser($_->[0], { type => 'P' }) });
} elsif ($_->[1] eq 'comm_invite_limit') {
push @bad, BML::ml('.error.limit', { user => LJ::ljuser($_->[0], { type => 'P' }) });
} else {
push @bad, BML::ml('.error.unknown', { user => LJ::ljuser($_->[0], { type => 'P' }) });
}
}
return LJ::bad_input(@bad);
}
# initialize lists of users to update and delete
# keyed on attribute type
my %add = ();
my %delete = ();
foreach (@allattribs) {
$add{$_} = {};
$delete{$_} = {};
}
# need a db handle now
my $dbh = LJ::get_db_writer();
# if we have $other_maints, then there are maintainers not in our
# current view, so they will not be modified, so the user can delete
# all maintainers from the current view
my $in = join(',', map { $dbh->quote($_) } @userids);
my $other_maints = $dbh->selectrow_array("SELECT COUNT(*) FROM reluser " .
"WHERE userid=? AND type='A' " .
"AND targetid NOT IN ($in)",
undef, $cid);
# users already in community
my $maints = 0;
my (%addr, %delr); # store additions and removals sorted by userid
foreach my $id (@userids) {
$id = $id + 0;
my $str;
foreach (@allattribs) {
if ($POST{"edit_${id}_$_"}) {
unless ($was{$id}->{$_}) {
$add{$_}->{$id} = 1;
$addr{$id}->{$_} = 1;
}
} else {
if ($was{$id}->{$_}) {
$delete{$_}->{$id} = 1;
$delr{$id}->{$_} = 1;
}
}
}
$maints++ if $POST{"edit_${id}_admin"};
}
# can't remove ALL maintainers, give them an error so they can
# go back and decide who to keep
if (! $other_maints && $maints < 1) {
$ret .= "<?h1 $ML{'Error'} h1?><?p Your community, " .
LJ::ljuser($cname, { 'type' => 'C' }) .
", must have at least one maintainer. " .
"Please <a href='" . BML::get_uri() . "?comm=$cname'>" .
"go back</a> and add a maintainer. p?>";
return $ret;
}
# delete members
if (%{$delete{'member'}}) {
# TAG:FR:bml_comm_members:del_members
LJ::remove_friend($cid, [ keys %{$delete{'member'}} ]);
}
# log maintainer deletions
foreach my $uid (keys %{$delete{admin} || {}}) {
$c->log_event('maintainer_remove', { actiontarget => $uid, remote => $remote });
}
# delete other rel edges
LJ::clear_rel_multi(
(map { [$cid, $_, 'A'] } keys %{$delete{admin} || {}}),
(map { [$cid, $_, 'P'] } keys %{$delete{post} || {}}),
(map { [$cid, $_, 'M'] } keys %{$delete{moderate} || {}}),
(map { [$cid, $_, 'N'] } keys %{$delete{preapprove} || {}}),
);
# perform additions
my @msgs;
if (%{$add{'member'}}) {
foreach my $id (keys %{$add{'member'}}) {
next if $was{$id}->{'member'};
my $u = LJ::load_userid($id);
if (LJ::u_equals($u, $remote)) {
# you're allowed to add yourself as member
LJ::join_community($remote, $c);
} else {
if (LJ::send_comm_invite($u, $c, $remote, [ 'member' ])) {
# if it succeeded, push the reinvited information
push @msgs, BML::ml('.reinvited2',
{ user => LJ::ljuser($u, { type => 'P' }),
aopts => "href='$LJ::SITEROOT/manage/invites.bml'" });
}
}
}
}
# log maintainer additions
foreach my $uid (keys %{$add{admin} || {}}) {
$c->log_event('maintainer_add', { actiontarget => $uid, remote => $remote });
}
# set rels in db/memcache
LJ::set_rel_multi( (map { [$cid, $_, 'A'] } keys %{$add{admin} || {}}),
(map { [$cid, $_, 'P'] } keys %{$add{post} || {}}),
(map { [$cid, $_, 'M'] } keys %{$add{moderate} || {}}),
(map { [$cid, $_, 'N'] } keys %{$add{preapprove} || {}}),
);
# create some other messages
my %done; # keep track of who we've done
foreach my $id (keys %addr, keys %delr) {
next if $done{$id}++;
my ($str, @astr, @dstr);
push @astr, $ML{"/manage/invites.bml.label.$_"}
foreach keys %{$addr{$id} || {}};
push @dstr, $ML{"/manage/invites.bml.label.$_"}
foreach keys %{$delr{$id} || {}};
$str .= "<li>" . BML::ml('.success.added', { list => join(', ', @astr) }) . "</li>\n" if @astr;
$str .= "<li>" . BML::ml('.success.deleted', { list => join(', ', @dstr) }) . "</li>\n" if @dstr;
push @msgs, LJ::ljuser($users{$id}, { type => 'P' }) . ":<ul>$str</ul>" if $str;
}
$ret .= "<?h1 $ML{'.success.header'} h1?>";
if (@msgs) {
$ret .= "<?p $ML{'.success.message2'} p?>\n<ul>";
$ret .= "<li>$_</li>\n" foreach @msgs;
$ret .= "</ul>";
}
if (@invited) {
$ret .= "<?p ";
$ret .= BML::ml('.success.invited2',
{ aopts => "href='$LJ::SITEROOT/manage/invites.bml'" });
$ret .= " p?><ul>";
foreach my $row (@invited) {
$ret .= "<li>" . LJ::ljuser($row->[0], { type => 'P' }) . ": ";
$ret .= "$ML{\"/manage/invites.bml.label.$_\"}, " foreach @{$row->[1] || []};
chop $ret; chop $ret;
$ret .= "</li>\n";
}
$ret .= "</ul>";
}
$ret .= "<?p $ML{'.success.nochanges'} p?>" unless @msgs || @invited;
$ret .= "<?p " . BML::ml(".success.return", { 'link' => BML::get_uri() . "?comm=$cname" }) . " p?>";
return $ret;
}
# browsing mode
# now get lists of: members, admins, able to post, moderators
my %users = ();
# need a dbr now
my $dbr = LJ::get_db_reader();
# get community members
# TAG:FR:bml_comm_members:get_members
my $sth = $dbr->prepare("SELECT u.userid, u.user FROM useridmap u, friends f " .
"WHERE u.userid=f.friendid AND f.userid=?");
$sth->execute($cid);
while (my ($id, $user) = $sth->fetchrow_array) {
$users{$id}->{'userid'} = $id;
$users{$id}->{'name'} = $user;
$users{$id}->{'member'} = 1;
}
my $sth = $dbr->prepare("SELECT r.targetid, r.type, u.user FROM reluser r, useridmap u " .
"WHERE r.targetid = u.userid AND r.userid=? AND r.type IN ('A','P','M','N')");
$sth->execute($cid);
my %count;
while (my ($id, $type, $user) = $sth->fetchrow_array) {
$users{$id}->{'userid'} = $id;
$users{$id}->{'name'} = $user;
my $key = {'A'=>'admin','P'=>'post','M'=>'moderate','N'=>'preapprove'}->{$type};
$users{$id}->{$key} = 1;
$count{$type}++;
}
# columns of our table, excluding username
my @attribs = ('member', 'post');
LJ::load_user_props($c, 'moderated');
push @attribs, ('preapprove')
if $c->{'moderated'} || $count{'N'};
push @attribs, ('moderate')
if $c->{'moderated'} || $count{'M'};
push @attribs, 'admin';
# sorting method;
my $method = $GET{'sort'};
my $cmp = sub {$a->{'name'} cmp $b->{'name'}};
$cmp = sub {$b->{'member'} <=> $a->{'member'}} if $method eq 'member';
$cmp = sub {$b->{'admin'} <=> $a->{'admin'}} if $method eq 'admin';
$cmp = sub {$b->{'post'} <=> $a->{'post'}} if $method eq 'post';
$cmp = sub {$b->{'moderate'} <=> $a->{'moderate'}} if $method eq 'moderate';
$cmp = sub {$b->{'preapprove'} <=> $a->{'preapprove'}} if $method eq 'preapprove';
my @users = sort $cmp values %users;
my $page_size = 100; # change to adjust page size
# are we going to jump to a specific user ?
my $jumppage;
my $jumpuser;
if (@users > $page_size && $POST{'jumpto'} =~ /^\w+$/) {
my $ct;
foreach (@users) {
$jumppage++ if $ct % $page_size == 0;
if ($POST{'jumpto'} eq $_->{'name'}) {
$jumpuser = $_->{'name'};
last;
}
$ct++;
}
undef $jumppage unless $jumpuser;
}
# how to make links back to this page
my $self_link = sub {
my $sort = "&sort=$GET{'sort'}" if $GET{'sort'};
return "members.bml?comm=$cname&page=$_[0]$sort";
};
my %items = BML::paging(\@users, $jumppage || $GET{'page'}, $page_size);
my $navbar = LJ::paging_bar($items{'page'}, $items{'pages'},
{ 'self_link' => $self_link });
@users = @{$items{'items'}};
# output starts here
$ret .= "<?p " . BML::ml('.name', { 'name' => LJ::ljuser($cname, { 'type' => 'C' }) });
$ret .= " " . BML::ml('.settings', { 'link' => "settings.bml?comm=$cname"}) . " p?>";
$ret .= "<form method='post' action='members.bml?comm=$cname'>";
$ret .= LJ::form_auth();
# jump to user
if ($items{'pages'} > 1) {
$ret .= "<div style='margin-left: 30px;'>Jump to user: ";
$ret .= LJ::html_text({ 'name' => 'jumpto', 'value' => $POST{'jumpto'},
'size' => '10', 'maxlength' => '15' }) . " ";
$ret .= LJ::html_submit(undef, 'Go') . "</div>";
$ret .= $navbar;
}
my $sortlink = BML::get_uri() . "?comm=$cname&sort=";
$ret .= "<br /><div align='center'><table class='borderedtable' cellpadding='2' cellspacing='0'>\n<tr>" .
"<th><a href='${sortlink}name'>$ML{'.key.user'}</a></th>";
$ret .= "<th><a href='${sortlink}$_'>".$ML{".key.$_"}."</a></th>" for (@attribs);
$ret .= "</tr>\n";
# rows for existing users
my $rc = 0;
my @wstrs;
foreach(@users) {
my $rstyle = ($rc++ & 1) ? "<?altcolor1?>" : "<?altcolor2?>";
$ret .= "<tr style='background-color: $rstyle;'><td>" . LJ::ljuser($_->{'name'}) . "</td>";
my $wstr;
foreach my $key (@attribs) {
$ret .= "<td align='center'>";
$ret .= LJ::html_check({ 'name' => "edit_$_->{'userid'}_$key",
'selected' => $_->{$key} });
$wstr .= $attrshort_r{$key} if $_->{$key};
$ret .= "</td>";
}
push @wstrs, "$_->{userid}:$_->{name}:$wstr" if $wstr;
$ret .= "</tr>\n";
}
# if on the last page, let users add to the list
if ($items{'page'} == $items{'pages'}) {
foreach(1..5) {
my $rstyle = ($rc++ & 1) ? "<?altcolor1?>" : "<?altcolor2?>";
$ret .= "<tr style='background-color: $rstyle;'><td>";
$ret .= LJ::html_text({ 'name' => "add_$_", 'size' => '10', 'maxlength' => '15' }) . "</td>";
foreach my $key (@attribs) {
$ret .= "<td align='center'>";
if ($key eq 'member' || $key eq 'post') {
$ret .= LJ::html_check({ name => "add_${_}_$key", selected => 1, });
} else {
$ret .= LJ::html_check({ name => "add_${_}_$key" });
}
$ret .= "</td>";
}
$ret .= "</tr>\n";
}
}
# some hidden values
$ret .= "</table>";
$ret .= LJ::html_hidden('ids', join(',', map { $_->{'userid'}} @users),
'attribs', join(',', @attribs),
'was', join(';', @wstrs)) . "\n";
$ret .= "<p>" . LJ::html_submit('action:update', $ML{'.update'}) . "</p>\n";
$ret .= "</form></div>\n\n";
$ret .= $navbar;
return $ret;
}
_code?>
<=body
page?>

View File

@@ -0,0 +1,425 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
{
use strict;
use vars qw(%GET %POST);
LJ::set_active_crumb('moderate');
return LJ::server_down_html() if ($LJ::SERVER_DOWN);
my $ret;
my $remote = LJ::get_remote();
unless ($remote) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'error.noremote'} p?>";
return $ret;
}
my $mode = $POST{'mode'};
my ($saved_comm, $saved_modid) = ("", 0);
if (LJ::did_post() && ($mode eq 'approve_do' || $mode eq 'reject_do')) {
my $cid = $POST{'cid'}+0;
my $modid = $POST{'modid'}+0;
my $c = LJ::load_userid($cid);
unless ($c) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.notfound'} p?>";
return $ret;
}
my $dbcm = LJ::get_cluster_master($c);
unless (LJ::check_rel($c, $remote, 'M')) {
$ret .= "<?h1 $ML{'Error'} h1?><?p " .
BML::ml('.error.noaccess', {'comm'=>"<?ljcomm $c->{'user'} ljcomm?>"}) . " p?>";
return $ret;
}
# use dbcm to read to minimize collisions between moderators due to replication lag
my $entry = $dbcm->selectrow_hashref("SELECT * FROM modlog WHERE journalid=? AND modid=?",
undef, $c->{'userid'}, $modid);
my $frozen = $dbcm->selectrow_array("SELECT request_stor FROM modblob WHERE journalid=? AND modid=?",
undef, $c->{'userid'}, $modid);
my $req = Storable::thaw($frozen) if $frozen;
unless ($frozen && $entry && $req->{'_moderate'}->{'authcode'} && $req->{'_moderate'}->{'authcode'} eq $POST{'auth'}) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.noentry'} p?>";
return $ret;
}
my $poster = LJ::load_userid($entry->{'posterid'});
my ($success, $do_mail) = (0, 0);
my $why_mail;
my $prot_err;
my $posturl;
if ($mode eq 'approve_do') {
my $res = LJ::Protocol::do_request('postevent', $req, \$prot_err, {'nomod'=>1, 'noauth'=>1});
if ($res) { # succeeded
$success = 1;
$ret .= "<?h1 $ML{'.posted.header'} h1?><?p $ML{'.posted.text'} p?>";
# does the poster want to know? if they have working email and notification on
($do_mail, $why_mail) = (1, 'success')
if ($poster->{'opt_gettalkemail'} eq "Y" && $poster->{'status'} eq "A");
$posturl = LJ::item_link($c, $res->{'itemid'}, $res->{'anum'}) . "\n\n";
} else {
$prot_err = LJ::Protocol::error_message($prot_err) if $prot_err;
$ret .= "<?h1 $ML{'Error'} h1?><?p " .
BML::ml('.posted.proterror', {'err'=>"<b>$prot_err</b>"}) . " p?>";
($do_mail, $why_mail) = (1, 'proterror')
if $poster->{'status'} eq "A";
}
if ($POST{'preapprove'}) {
LJ::set_rel($c, $poster, 'N');
$ret .= "<?h1 $ML{'.posted.appheader'} h1?><?p " .
BML::ml('.posted.apptext', {'user'=>"<?ljuser $poster->{'user'} ljuser?>"}) . " p?>";
}
}
if ($mode eq 'reject_do') {
$success = 1;
$ret .= "<?h1 $ML{'.rejected.header'} h1?><?p $ML{'.rejected.text'} p?>";
($do_mail, $why_mail) = (1, 'reject')
if $poster->{'status'} eq 'A';
}
$do_mail = 1 unless $poster->{'statusvis'} eq 'V';
# mail the poster
if ($do_mail) {
my $subject="Moderated submission notification";
my $to = $poster->{'email'};
my $body = "Your message submitted to the moderated community $c->{'user'} ";
if ($why_mail eq 'success') {
$body .= "has been approved and successfully posted.\n\n$posturl";
} elsif ($why_mail eq 'proterror') {
$body .= "has been approved, but failed to be posted due to the following protocol error:\n$prot_err\n\n";
} elsif ($why_mail eq 'reject') {
$body .= "has been rejected by a moderator of that community.\n\nPlease note that replies to this email are not sent to the community's moderator(s). If you would like to discuss the reasons for your entry's rejection, you will need to contact a moderator directly.\n\n";
}
if ($why_mail eq 'reject' && $POST{'why'}) {
$body .= "Here are the reasons for the rejection as provided by the moderator: \n\n" . $POST{'why'} . "\n\n";
}
unless ($why_mail eq 'success') {
$body .= "This is the message you submitted:\n\n";
my $etime = sprintf("%04d-%02d-%02d %02d:%02d",
$req->{'year'}, $req->{'mon'},
$req->{'day'}, $req->{'hour'},
$req->{'min'});
$body .= "Time: $etime\n";
$body .= "Subject: " . $req->{'subject'} . "\n";
if ($req->{'props'}->{'current_music'}) {
$body .= "Current Music: " . $req->{'props'}->{'current_music'} . "\n";
}
if ($req->{'props'}->{'current_mood'}) {
$body .= "Current Mood: " . $req->{'props'}->{'current_mood'} . "\n";
}
if ($req->{'props'}->{'picture_keyword'}) {
$body .= "Picture Keyword: " . $req->{'props'}->{'picture_keyword'} . "\n";
}
$body .= "Text:\n" . $req->{'event'} . "\n\n";
$body .= "Regards,\n$LJ::SITENAME Team\n\n$LJ::SITEROOT/\n";
}
LJ::send_mail({
'to' => $to,
'from' => $LJ::BOGUS_EMAIL,
'charset' => 'utf-8',
'subject' => $subject,
'body' => $body,
});
}
if ($success) {
$saved_comm = $c->{'user'};
$saved_modid = 0;
# Delete this moderated entry from the list
$c->do("DELETE FROM modlog WHERE journalid=? AND modid=?",
undef, $c->{'userid'}, $modid);
$c->do("DELETE FROM modblob WHERE journalid=? AND modid=?",
undef, $c->{'userid'}, $modid);
# FALL THROUGH to showing the list of entries in this community
} else {
$ret .= "<p>";
$ret .= BML::ml('Backlink', {
'link'=>'/community/manage.bml',
'text'=>$ML{'.manage'},
}) . "<br />";
$ret .= BML::ml('Backlink', {
'link'=>"/community/moderate.bml?comm=$c->{'user'}",
'text'=>$ML{'.moderate'},
}) . "<br />";
$ret .= "</p>";
return $ret;
}
}
my $comm = $saved_comm || ($mode eq 'action' ? $POST{'comm'} : $GET{'comm'});
my $modid = $saved_modid || ($mode eq 'action' ? $POST{'modid'} : $GET{'modid'});
$modid += 0;
# redirect to community/manage if a GET request with no arg
return BML::redirect("manage.bml") unless $comm;
my $c = LJ::load_user($comm);
unless ($c) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.notfound'} p?>";
return $ret;
}
my $dbcr = LJ::get_cluster_def_reader($c);
unless (LJ::check_rel($c, $remote, 'M')) {
$ret .= "<?h1 $ML{'Error'} h1?><?p " .
BML::ml('.error.noaccess', {'comm'=>"<?ljcomm $comm ljcomm?>"}) . " p?>";
return $ret;
}
if ($mode eq 'action' && $POST{'action:approve'}) {
my ($posterid, $frreq);
if ($modid) {
($posterid, $frreq) = $dbcr->selectrow_array
("SELECT l.posterid, b.request_stor FROM modlog l, modblob b " .
"WHERE l.journalid=? AND l.modid=? AND l.journalid=b.journalid AND l.modid=b.modid",
undef, $c->{'userid'}, $modid);
}
unless ($posterid) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.noentry'} p?>";
return $ret;
}
my $req = Storable::thaw($frreq);
my $poster = LJ::load_userid($posterid);
$ret .= "<?h1 $ML{'.approve.header'} h1?><?p $ML{'.approve.text'} p?>";
$ret .= "<p><form method='post' action='/community/moderate.bml'><center>";
$ret .= "<input type='hidden' name='mode' value='approve_do'>";
$ret .= "<input type='hidden' name='cid' value='$c->{'userid'}'>";
$ret .= "<input type='hidden' name='modid' value='$modid'>";
$ret .= "<input type='hidden' name='auth' value='" . $req->{'_moderate'}->{'authcode'} . "'>";
$ret .= "<input type='submit' value='$ML{'.approve.button'}'>";
$ret .= "</center>";
$ret .= "<p><input type='checkbox' name='preapprove'> " .
BML::ml('.approve.preapprove', {'user'=>"<?ljuser $poster->{'user'} ljuser?>"});
$ret .= "</form>";
return $ret;
}
if ($mode eq 'action' && $POST{'action:reject'}) {
my ($posterid, $frreq);
if ($modid) {
($posterid, $frreq) = $dbcr->selectrow_array
("SELECT l.posterid, b.request_stor FROM modlog l, modblob b " .
"WHERE l.journalid=? AND l.modid=? AND l.journalid=b.journalid AND l.modid=b.modid",
undef, $c->{'userid'}, $modid);
}
unless ($posterid) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.noentry'} p?>";
return $ret;
}
my $req = Storable::thaw($frreq);
my $poster = LJ::load_userid($posterid);
$ret .= "<?h1 $ML{'.reject.header'} h1?><?p $ML{'.reject.text'} p?>";
$ret .= "<p><form method='post' action='/community/moderate.bml'><center>";
$ret .= "<input type='hidden' name='mode' value='reject_do'>";
$ret .= "<input type='hidden' name='cid' value='$c->{'userid'}'>";
$ret .= "<input type='hidden' name='modid' value='$modid'>";
$ret .= "<input type='hidden' name='auth' value='" . $req->{'_moderate'}->{'authcode'} . "'>";
$ret .= "<input type='submit' value='$ML{'.reject.button'}'>";
$ret .= "</center>";
if ($poster->{'status'} eq 'A') {
$ret .= "<?p $ML{'.reject.reason'} p?>";
$ret .= "<?p <textarea rows='10' cols='60' wrap='soft' name='why'></textarea> p?>";
}
$ret .= "</form>";
return $ret;
}
# browse either the list of entries or an entry
unless ($modid) {
# present a list of entries
$ret .= "<?h1 $ML{'.browse.header'} h1?><?p " .
BML::ml('.browse.text', {'link'=>"<?ljcomm $comm ljcomm?>"}) . " p?>";
$ret .= "<div style='margin: 10px 0 10px 40px'>";
my @entries;
my $sth = $dbcr->prepare("SELECT * FROM modlog WHERE journalid=$c->{'userid'}");
$sth->execute;
while ($_ = $sth->fetchrow_hashref) {
push @entries, $_;
}
unless (@entries) {
$ret .= "<i>$ML{'.browse.empty'}</i>";
} else {
$ret .= "<table cellpadding='5'><tr><td><b>$ML{'.brlist.time'}</b></td>" .
"<td><b>$ML{'.brlist.poster'}</b></td><td><b>$ML{'.brlist.subject'}</b></td>" .
"<td><b>$ML{'.brlist.actions'}</b></td></tr>";
my %users;
LJ::load_userids_multiple([ map { $_->{'posterid'}, \$users{$_->{'posterid'}} } @entries ]);
foreach (sort { $a->{'logtime'} lt $b->{'logtime'} } @entries) {
my $link = "/community/moderate.bml?comm=$comm&amp;modid=" . $_->{'modid'};
my $subject = $_->{'subject'} ? LJ::eall($_->{'subject'}) : "<i>[No Subject]</i>";
$ret .= "<tr><td>$_->{'logtime'}</td>" .
"<td><?ljuser " . $users{$_->{'posterid'}}->{'user'} . " ljuser?></td>" .
"<td><a href='$link'>$subject</a></td>" .
"<td>" .
BML::ml('Actionlink', {
'link'=>"<a href='$link'>$ML{'.brlist.view'}</a>"
}) . "</td></tr>";
}
$ret .= "</table>";
}
$ret .= "</div>";
$ret .= BML::ml('Backlink', {
'link'=>'/community/manage.bml',
'text'=>$ML{'.manage'},
}) . "<br />";
} else {
# view an entry
my $frozen = $dbcr->selectrow_array("SELECT request_stor FROM modblob WHERE journalid=? AND modid=?",
undef, $c->{'userid'}, $modid);
unless ($frozen) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.noentry'} p?>";
return $ret;
}
my $req = Storable::thaw($frozen);
# cleaning
my $event = $req->{'event'};
$event =~ s/^\s+//;
$event =~ s/\s+$//;
if ($req->{'lineendings'} eq "mac") {
$event =~ s/\r/\n/g;
} else {
$event =~ s/\r//g;
}
my $etime = sprintf("%04d-%02d-%02d %02d:%02d",
$req->{'year'}, $req->{'mon'},
$req->{'day'}, $req->{'hour'},
$req->{'min'});
my $subject = $req->{'subject'};
my $props = $req->{'props'};
my $up = LJ::load_user($req->{'username'});
my $posterid = $up->{'userid'};
my $error;
my @polls = LJ::Poll::parse(\$event, \$error, {
'journalid' => $c->{'userid'},
'posterid' => $posterid,
});
$event =~ s/<lj-poll-placeholder>/LJ::Poll::preview(shift @polls);/eg;
LJ::CleanHTML::clean_event(\$event, {'preformatted' => $req->{'props'}->{'opt_preformatted'},
'cutpreview' => 1,
'cuturl' => '#',
});
BML::ebml(\$event);
$ret .= "<p>";
$ret .= BML::ml('Backlink', {
'link'=>'/community/manage.bml',
'text'=>$ML{'.manage'},
}) . "<br />";
$ret .= BML::ml('Backlink', {
'link'=>"/community/moderate.bml?comm=$comm",
'text'=>$ML{'.moderate'},
}) . "<br />";
$ret .= "</p>";
$ret .= "<p>";
$ret .= "<table><tr valign='middle'>";
my $picid = LJ::get_picid_from_keyword($up, $props->{'picture_keyword'});
my %userpics;
if ($picid) {
LJ::load_userpics(\%userpics, [ $up, $picid ]);
my $alt = $up->{'name'};
if ($props->{'picture_keyword'}) {
$alt .= ": $props->{'picture_keyword'}";
}
$alt = LJ::ehtml($alt);
$ret .= "<td><img src='$LJ::USERPIC_ROOT/$picid/$up->{'userid'}' width='$userpics{$picid}->{'width'}' ".
"height='$userpics{$picid}->{'height'}' align='absmiddle' ".
"hspace='3' title='$alt' alt=''></td>";
}
$ret .= "<td>";
$ret .= BML::ml("talk.somebodywrote_comm", { 'realname' => BML::eall($up->{'name'}),
'userlink' => LJ::ljuser($up->{'user'}),
'commlink' => "<?ljcomm $c->{'user'} ljcomm?>" });
$etime =~ s!(\d\d\d\d)-(\d\d)-(\d\d)!LJ::date_to_view_links($c, $&)!e;
$ret .= "<br /><font size='-1'>@ $etime</font>";
$ret .= "</td></tr></table>";
my $actions .= "<input type='hidden' name='mode' value='action' />";
$actions .= "<input type='hidden' name='comm' value='$comm' />";
$actions .= "<input type='hidden' name='modid' value='$modid' />";
$actions .= "<input type='submit' name='action:approve' value='$ML{'.choice.approve'}' style='font-size: 15pt; background: $ML{'.choice.bkapprove'}; color: #000000' />";
$actions .= "&nbsp;&nbsp;<input type='submit' name='action:reject' value='$ML{'.choice.reject'}' style='font-size: 15pt; background: $ML{'.choice.bkreject'}; color: #000000' />";
$ret .= "<form method='post' action='/community/moderate.bml'>";
$ret .= BML::fill_template("standout", {'DATA'=> $actions});
$ret .= "</form>";
my %current;
if ($props->{'current_mood'} || $props->{'current_moodid'}) {
$current{'Mood'} = $props->{'current_mood'};
LJ::CleanHTML::clean_subject(\$current{'Mood'});
my $mid = $props->{'current_moodid'};
if ($mid) {
my $theme = $up->{'moodthemeid'};
my %pic;
my $name = LJ::mood_name($mid);
if (LJ::get_mood_picture($theme, $mid, \%pic)) {
$current{'Mood'} = "<img src='$pic{'pic'}' align='absmiddle' width='$pic{'w'}' height='$pic{'h'}' vspace='1'> $name";
} else {
$current{'Mood'} = $name;
}
}
}
if ($props->{'current_music'}) {
$current{'Music'} = $props->{'current_music'};
LJ::CleanHTML::clean_subject(\$current{'Music'});
}
$ret .= "<div style='margin-left: 30px'>";
if (%current)
{
$ret .= "<table border=0>\n";
foreach (sort keys %current) {
my $curkey = "talk.curname_" . $_;
my $curname = BML::ml($curkey);
$curname = "<b>Current $_:</b>" unless $curname;
$ret .= "<tr><td align=right>$curname</td><td>$current{$_}</td></tr>\n";
}
$ret .= "</table><p>\n";
}
### security indicator
my $sec = "";
if ($req->{'security'} eq "private") {
$sec = "<?securityprivate?>";
} elsif ($req->{'security'} eq "usemask") {
$sec = "<?securityprotected?>";
}
$sec .= "<br />\n" unless $sec eq "" or $subject;
$ret .= $sec;
if ($subject) {
LJ::CleanHTML::clean_subject(\$subject);
BML::ebml(\$subject);
$ret .= "<font face='Arial,Helvetica' size='+1'><i><b>$subject</b></i></font><br />\n";
}
$ret .= $event;
$ret .= "</div>";
$ret .= "<br clear='all' /><hr width='100%' size='2' align='center' />";
}
return $ret;
}
_code?>
<=body
page?>

View File

@@ -0,0 +1,135 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
{
use strict;
use vars qw(%GET %POST);
LJ::set_active_crumb('commpending');
return LJ::server_down_html() if $LJ::SERVER_DOWN;
# always have links at top
my $ret = BML::ml('Backlink', {
'link' => '/community/manage.bml',
'text' => $ML{'/community/members.bml.manage2'},
});
# get remote
my $remote = LJ::get_remote();
return "<?h1 $ML{'Error'} h1?><?p $ML{'error.noremote'} p?>"
unless $remote;
my $cname = $GET{'comm'};
return BML::redirect("$LJ::SITEROOT/community/manage.bml") unless $cname;
# get $c object
my $c = LJ::load_user($cname);
return "<?h1 $ML{'Error'} h1?><?p $ML{'/community/members.bml.error.nocomm'} p?>"
unless $c;
my $cid = $c->{'userid'};
# is $remote an admin?
unless (LJ::can_manage($remote, $c)) {
$ret .= "<?h1 $ML{'Error'} h1?><?p ";
$ret .= BML::ml('/community/members.bml.error.noaccess',
{ comm => LJ::ljuser($cname, { type => 'C' }) });
$ret .= " p?>";
return $ret;
}
# hit up the database to find pending members
my $pendids = LJ::get_pending_members($c) || [];
my $us = LJ::load_userids(@$pendids);
# nothing pending?
return "<?h1 $ML{'.nopending.title'} h1?><?p $ML{'.nopending.body'} p?>"
unless @$pendids || LJ::did_post();
# saving a form submission
if ($POST{'action:update'}) {
my @userids = split(',', $POST{'ids'});
# need a db handle now
my $dbh = LJ::get_db_writer();
# hit up each user to find out what to do with them
my ($added, $rejected, $ignored, $previous);
foreach my $id (@userids) {
unless ($us->{$id}) {
$previous++;
next;
}
if ($POST{"pending_$id"} eq 'yes') {
LJ::approve_pending_member($cid, $id);
$added++;
} elsif ($POST{"pending_$id"} eq 'no') {
LJ::reject_pending_member($cid, $id);
$rejected++;
} else {
$ignored++;
}
}
$ret .= "<?h1 $ML{'/community/members.bml.success.header'} h1?><?p $ML{'/community/members.bml.success.message'} p?>";
$ret .= "<?p " . BML::ml('.success.added', { num => $added }) . " p?>" if $added;
$ret .= "<?p " . BML::ml('.success.rejected', { num => $rejected }) . " p?>" if $rejected;
$ret .= "<?p " . BML::ml('.success.ignored', { num => $ignored }) . " p?>" if $ignored;
$ret .= "<?p " . BML::ml('.success.previous', { num => $previous }) . " p?>" if $previous;
$ret .= "<?p " . BML::ml("/community/members.bml.success.return", { 'link' => BML::get_uri() . "?comm=$cname" }) . " p?>";
return $ret;
}
my @users = sort { $a->{user} cmp $b->{user} } values %$us;
my $page_size = 100; # change to adjust page size
# how to make links back to this page
my $self_link = sub {
return "pending.bml?comm=$cname&page=$_[0]";
};
my %items = BML::paging(\@users, $GET{page}, $page_size);
my $navbar = LJ::paging_bar($items{page}, $items{pages},
{ self_link => $self_link });
@users = @{$items{items}};
# output starts here
$ret .= "<?p " . BML::ml('/community/members.bml.name', { name => LJ::ljuser($cname, { type => 'C' }) });
$ret .= " " . BML::ml('/community/members.bml.settings', { 'link' => "settings.bml?comm=$cname"}) . " p?>";
$ret .= "<form method='post' action='pending.bml?comm=$cname'>";
# table headers
$ret .= "<br /><div align='center'><table class='borderedtable' cellspacing='0' cellpadding='2'>\n<tr>" .
"<th>$ML{'/community/members.bml.key.user'}</th><th colspan='2'>$ML{'.approve.title'}</th></tr>\n";
# rows for existing users
my $rc = 0;
foreach (@users) {
my $rstyle = ($rc++ & 1) ? '<?altcolor1?>' : '<?altcolor2?>';
$ret .= "<tr style='background-color: $rstyle;'><td>" . LJ::ljuser($_->{user}) . "</td>";
$ret .= "<td>" . LJ::html_check({ type => 'radio', name => "pending_$_->{userid}",
id => "pending_$_->{userid}_yes", value => 'yes' });
$ret .= " <label for='pending_$_->{userid}_yes'>$ML{'.yes'}</label></td>\n";
$ret .= "<td>" . LJ::html_check({ type => 'radio', name => "pending_$_->{userid}",
id => "pending_$_->{userid}_no", value => 'no' });
$ret .= " <label for='pending_$_->{userid}_no'>$ML{'.no'}</label></td>\n";
$ret .= "</tr>\n";
}
# some hidden values
$ret .= LJ::html_hidden('ids', join(',', map { $_->{userid}} @users)) . "\n";
$ret .= "</table><p>" . LJ::html_submit('action:update', $ML{'/community/members.bml.update'}) . "</p>\n";
$ret .= "</div></form>\n\n";
$ret .= $navbar;
return $ret;
}
_code?>
<=body
page?>

View File

@@ -0,0 +1,165 @@
<?_info
localblocks<=
crit<=
{F}
<tr bgcolor='<?emcolor?>'>
<td align='left' colspan='2'><b>%%name%%</b>
</tr>
<tr><td>&nbsp;</td><td>%%form%%</td></tr>
<=crit
bar<=
{F}
<tr bgcolor='<?emcolor?>'>
<td>&nbsp;</td>
<td><b>%%name%%</b>
</tr>
<tr><td>&nbsp;</td><td>%%form%%</td></tr>
<=bar
<=localblocks
_info?><?page
title=><?_ml .title _ml?>
body<=
<?h1 <?_ml .label.searchcomm _ml?> h1?>
<?p
<?_ml .label.selecriteria _ml?>
p?>
<?hr?>
<CENTER>
<form action="/directory.bml">
<input type='hidden' name="com_do" value='1' />
<table cellpadding='4' cellspacing='0' border='0'>
<!--- location --->
<?crit
name=><?_ml .label.bylocation _ml?>
code=>s_loc
form<=
<table>
<tr><td align='right'><?_ml .label.country _ml?></td><td>
<?_code
{
use strict;
LJ::set_active_crumb('commsearch');
my %countries = ();
LJ::load_codes({ "country" => \%countries });
return LJ::html_select({ 'name' => 'loc_cn', },
'', '',
map { $_, $countries{$_} }
("US", sort { $countries{$a} cmp $countries{$b} } keys %countries));
}
_code?>
</td></tr>
<tr><td align='right'><?_ml .label.stateprovince _ml?></td><td><input name="loc_st" /></td></tr>
<tr><td align='right'><?_ml .label.city _ml?></td><td><input name="loc_ci" /></td></tr>
</table>
<=form
crit?>
<!---- update time ----->
<?crit
name=><?_ml .label.bytime _ml?>
code=>s_ut
form<=
<?_ml .label.updated _ml?>
<select name="ut_days">
<option value="">-------</option>
<option value="1"><?_ml .sel.day _ml?></option>
<option value="7"><?_ml .sel.week _ml?></option>
<option value="30"><?_ml .sel.month _ml?></option>
</select>
<=form
crit?>
<!---- interest ----->
<?crit
name=><?_ml .label.byinterest _ml?>
code=>s_int
form<=
Community interest: <input name="int_like" size='30' />
<=form
crit?>
<!---- has member ----->
<?crit
name=><?_ml .label.hasmember _ml?>
code=>s_fr
form<=
Contains user <input name="fr_user" size='15' maxlength='15' /> as a member.
<=form
crit?>
<?bar
name=><?_ml .label.othercriteria _ml?>
form<=
<input type='checkbox' name="s_withpic" value='1' /> <?_ml .checkbox.onlywithpics _ml?>
<=form
bar?>
<!---- output formatting ----->
<?bar
name=><?_ml .label.displayoptions _ml?>
form<=
<table>
<tr>
<td align='right'>
<?_ml .label.outputformat _ml?>
</td><td>
<select name="opt_format">
<option value="com" selected='selected'><?_ml .sel.commview _ml?></option>
<option value="pics"><?_ml .sel.bypicture _ml?></option>
<option value="simple"><?_ml .sel.simple _ml?></option>
</select>
</td></tr>
<tr>
<td align='right'>
<?_ml .label.sortmethod _ml?>
</td><td>
<select name="opt_sort">
<option value="user" selected='selected'><?_ml .sel.username _ml?></option>
<option value="name"><?_ml .sel.communityname _ml?></option>
<option value="ut"><?_ml .sel.updatetime _ml?></option>
</select>
</td></tr>
<tr>
<td align='right'>
<?_ml .label.records _ml?>
</td><td>
<select name="opt_pagesize">
<option value="25">25</option>
<option value="50">50</option>
<option value="100" selected='selected'>100</option>
<option value="200">200</option>
</select>
</td></tr>
</table>
<=form
bar?>
<!--- submit --->
<tr bgcolor='<?emcolor?>'><td colspan='2' align='center'>
<input type='submit' value="<?_ml .button.search _ml?>" />
<input type='reset' value="<?_ml .button.clear _ml?>" />
</td></tr>
</table>
</form>
</center>
<=body
page?>
<?_c <LJDEP>
form: htdocs/directory.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,208 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
{
use strict;
use vars qw(%GET %POST);
LJ::set_active_crumb('commsentinvites');
return LJ::server_down_html() if $LJ::SERVER_DOWN;
# always have links at top
my $ret = BML::ml('Backlink', {
'link' => '/community/manage.bml',
'text' => $ML{'/community/members.bml.manage2'},
});
# get remote
my $remote = LJ::get_remote();
unless ($remote) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'error.noremote'} p?>";
return $ret;
}
my $cname = $GET{comm};
return BML::redirect("$LJ::SITEROOT/community/manage.bml") unless $cname;
# get $c object
my $c = LJ::load_user($cname);
unless ($c) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'/community/members.bml.error.nocomm'} p?>";
return $ret;
}
my $cid = $c->{'userid'};
# is $remote an admin?
unless (LJ::can_manage_other($remote, $c)) {
$ret .= "<?h1 $ML{'Error'} h1?><?p ";
$ret .= BML::ml('/community/members.bml.error.noaccess',
{ 'comm' => LJ::ljuser($cname, { 'type' => 'C' }) });
$ret .= " p?>";
return $ret;
}
# columns of our table, excluding username
my @attribs = ('post');
my @titleattribs = ('P');
LJ::load_user_props($c, 'moderated');
if ($c->{moderated}) {
push @attribs, ('preapprove', 'moderate');
push @titleattribs, ('U', 'M');
}
push @attribs, 'admin';
push @titleattribs, 'A';
# now get sent invites and the users involved
my $sent = LJ::get_sent_invites($c) || [];
my @ids;
push @ids, ($_->{userid}, $_->{maintid}) foreach @$sent;
my $us = LJ::load_userids(@ids);
# populate %users hash
my %users = ();
foreach my $invite (@$sent) {
my $id = $invite->{userid};
next if $GET{show} && $GET{show} ne 'all' && $invite->{status} ne $GET{show};
$users{$id}{userid} = $id;
$users{$id}{maintainer} = $us->{$invite->{maintid}}{user};
$users{$id}{user} = $us->{$id}{user};
$users{$id}{$_} = 1 foreach keys %{$invite->{args}};
$users{$id}{status} = $invite->{status};
$users{$id}{date} = LJ::mysql_time($invite->{recvtime});
$users{$id}{date} =~ s/\s.+$//; # we just want the date, ignore time
}
# hop out if there were no invites
unless (%users) {
return "$ret<?h1 $ML{'.none.title'} h1?><?p " .
BML::ml('.none.body', { aopts => "href='/community/members.bml?comm=$c->{user}'" }) .
" p?>";
}
# sorting method;
my $method = $GET{'sort'};
my $cmp = sub {$a->{'user'} cmp $b->{'user'}};
$cmp = sub {$b->{'admin'} <=> $a->{'admin'}} if $method eq 'A';
$cmp = sub {$b->{'post'} <=> $a->{'post'}} if $method eq 'P';
$cmp = sub {$b->{'moderate'} <=> $a->{'moderate'}} if $method eq 'M';
$cmp = sub {$b->{'preapprove'} <=> $a->{'preapprove'}} if $method eq 'U';
$cmp = sub {$a->{'status'} cmp $b->{'status'}} if $method eq 'status';
$cmp = sub {$b->{'date'} cmp $a->{'date'}} if $method eq 'date';
$cmp = sub {$b->{'maintainer'} cmp $a->{'maintainer'}} if $method eq 'maint';
my @users = sort $cmp values %users;
my $page_size = 100; # change to adjust page size
# are we going to jump to a specific user ?
my $jumppage;
my $jumpuser;
if (@users > $page_size && $POST{'jumpto'} =~ /^\w+$/) {
my $ct;
foreach (@users) {
$jumppage++ if $ct % $page_size == 0;
if ($POST{'jumpto'} eq $_->{'name'}) {
$jumpuser = $_->{'name'};
last;
}
$ct++;
}
undef $jumppage unless $jumpuser;
}
# how to make links back to this page
my $self_link = sub {
my $sort = "&sort=$GET{'sort'}" if $GET{'sort'};
my $filter = "&show=$GET{'show'}" if $GET{'show'};
return "sentinvites.bml?comm=$cname&page=$_[0]$sort$filter";
};
my %items = BML::paging(\@users, $jumppage || $GET{'page'}, $page_size);
my $navbar = LJ::paging_bar($items{'page'}, $items{'pages'},
{ 'self_link' => $self_link });
@users = @{$items{'items'}};
# output starts here
$ret .= "<?p " . BML::ml('/community/members.bml.name', { 'name' => LJ::ljuser($cname, { 'type' => 'C' }) });
$ret .= " " . BML::ml('/community/members.bml.settings', { 'link' => "settings.bml?comm=$cname"}) . " p?>";
# jump to user
if ($items{'pages'} > 1) {
$ret .= "<form method='post' action='sentinvites.bml?comm=$cname'>";
$ret .= "<div style='margin-left: 30px;'>Jump to user: ";
$ret .= LJ::html_text({ 'name' => 'jumpto', 'value' => $POST{'jumpto'},
'size' => '10', 'maxlength' => '15' }) . " ";
$ret .= LJ::html_submit(undef, 'Go') . "</div>";
$ret .= "</form>";
$ret .= $navbar;
}
# setup showlinks
my $showlinks = "<form method='get' action='/community/sentinvites.bml'>";
$showlinks .= LJ::html_hidden('comm', $c->{user});
$showlinks .= "Filter to: ";
$showlinks .= LJ::html_select({ name => 'show', selected => $GET{show} }, map { $_, $_ } qw(all accepted outstanding rejected));
$showlinks .= LJ::html_submit(undef, 'Refresh');
$showlinks .= "</form>";
my $filter = "&show=$GET{show}" if $GET{show};
my $sortlink = BML::get_uri() . "?comm=$cname$filter&sort=";
$ret .= "<br /><div align='center'>$showlinks";
$ret .= <<END;
<table class='borderedtable' cellpadding='2' cellspacing='0'>
<tr style="background-color: <?altcolor1?>; border-bottom: solid 1px black;">
<th style='border-right: solid 1px black;'>P</th>
<td style='border-bottom: solid 1px black; border-right: solid 1px black;'>Posting Access</td>
<th style='border-right: solid 1px black;'>U</th>
<td style='border-bottom: solid 1px black;'>Unmoderated</td>
</tr>
<tr style="background-color: <?altcolor2?>; border-bottom: solid 1px black;">
<th style='border-right: solid 1px black; border-bottom: none;'>M</th>
<td style='border-right: solid 1px black;'>Moderator</td>
<th style='border-right: solid 1px black; border-bottom: none;'>A</th>
<td>Maintainer</td>
</tr>
</table>
<br />
END
$ret .= "<table class='borderedtable' cellpadding='2' cellspacing='0'>\n<tr>" .
"<th><a href='${sortlink}name'>$ML{'/community/members.bml.key.user'}</a></th>";
$ret .= "<th><a href='${sortlink}$_'>$_</a></th>" foreach @titleattribs;
$ret .= "<th><a href='${sortlink}maint'>$ML{'.key.sentby'}</a></th>";
$ret .= "<th><a href='${sortlink}date'>$ML{'.key.date'}</a></th>";
$ret .= "<th><a href='${sortlink}status'>$ML{'.key.status'}</a></th>";
$ret .= "</tr>\n";
# checkboxes and such
my $yes = '<img src="/img/blue_check.gif" width="15" height="15" border="0">';
my $no = '-';
# rows for existing users
my $rc = 0;
foreach(@users) {
my $rstyle = ($rc++ & 1) ? "<?altcolor1?>" : "<?altcolor2?>";
$ret .= "<tr style='background-color: $rstyle;'><td>" . LJ::ljuser($_->{user}) . "</td>";
foreach my $key (@attribs) {
$ret .= "<td align='center'>";
$ret .= $_->{$key} ? $yes : $no;
$ret .= "</td>";
}
$ret .= "<td>" . LJ::ljuser($_->{maintainer}, { type => 'P' }) . "</td>";
$ret .= "<td>$_->{date}</td>";
$ret .= "<td>$_->{status}</td>";
$ret .= "</tr>\n";
}
$ret .= "</table>\n";
$ret .= "<br /><a href='/community/members.bml?comm=$c->{user}'>$ML{'.send'}</a>";
$ret .= "</div>\n\n";
$ret .= $navbar;
return $ret;
}
_code?>
<=body
page?>

View File

@@ -0,0 +1,252 @@
<?page
title<=
<?_code
if ($GET{'mode'} eq 'create') {
return $ML{'.title.create'};
}
else {
return $ML{'.title.modify'};
}
_code?>
<=title
head<=
<style type='text/css'>
div.opts { margin: 10px 0 10px 30px; }
</style>
<=head
body<=
<?_code
use strict;
use vars qw(%GET %POST);
# always have links at top
my $ret = BML::ml('Backlink', {
'link' => '/community/manage.bml',
'text' => $ML{'/community/members.bml.manage2'},
});
my %errors;
my $remote = LJ::get_remote();
unless ($remote) {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'error.noremote'} p?>";
return $ret;
}
unless ($remote->{'journaltype'} eq 'P') {
$ret .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.maintainertype'} p?>";
return $ret;
}
my $mode = "modify";
$mode = "create" if $GET{'mode'} eq 'create';
if (LJ::did_post())
{
my $sth;
my $cuser = LJ::canonical_username($POST{'cuser'});
my $cu = LJ::load_user($cuser);
unless ($cu) {
$errors{'username'} = $ML{'.error.notfound'};
}
if ($cu && $cu->{'userid'} == $remote->{'userid'}) {
$errors{'username'} = $ML{'.error.samenames'};
}
# if we're changing rather than creating, check that we can
if ($mode eq 'modify' && !LJ::can_manage_other($remote, $cu)) {
$errors{'username'} = BML::ml('.error.noaccess', {'comm'=>$cuser});
}
# if we're creating, community password must match
if ($mode eq 'create' && $cu && !LJ::auth_okay($cu, $POST{'cpassword'})) {
$errors{'password'} = $ML{'.error.badpassword'};
}
# disallow changing the journal type if the journal has entries
if ($mode eq 'create' && !%errors && !LJ::check_priv($remote, "changejournaltype", "")) {
my $count;
my $userid=$cu->{'userid'}+0;
my $dbcr = LJ::get_cluster_reader($cu);
$count = $dbcr->selectrow_array("SELECT COUNT(*) FROM log2 WHERE journalid=$userid AND posterid=journalid");
$errors{'username'} = $ML{'.error.hasentries'} if $count;
}
# if it's already a community, don't let them turn it into a community
if ($mode eq 'create' && !%errors && $cu->{journaltype} eq 'C') {
$errors{'username'} = $ML{'.error.alreadycomm'};
}
# if we found errors, we'll redisplay the form below. otherwise,
# proceed.
unless (%errors) {
my $dbh = LJ::get_db_writer();
my $cid = $cu->{'userid'};
my $qmembership = $POST{membership};
$qmembership = 'closed' unless $qmembership =~ m/(?:open|moderated|closed)/;
$qmembership = $dbh->quote($qmembership);
my $qpostlevel = $dbh->quote($POST{'postlevel'} eq "members" ? "members" : "select");
LJ::update_user($cu, { journaltype => 'C' });
if ($mode eq 'create') {
$dbh->do("REPLACE INTO community (userid, membership, postlevel) VALUES ($cid, $qmembership, $qpostlevel)");
LJ::set_rel($cu, $remote, 'A');
# delete existing friends
my $friends = LJ::get_friends($cid, undef, undef, 'force') || {};
LJ::remove_friend($cid, [ keys %$friends ]);
} else {
$dbh->do("UPDATE community SET membership=$qmembership, postlevel=$qpostlevel WHERE userid=$cid");
}
my $nonmembers = $POST{'nonmember_posting'} + 0;
my $moderated = $POST{'moderated'} + 0;
LJ::set_userprop($cu, 'nonmember_posting', $nonmembers);
LJ::set_userprop($cu, 'moderated', $moderated);
if ($moderated && ! LJ::load_rel_user($cu->{'userid'}, 'M')->[0]) {
LJ::set_rel($cu->{'userid'}, $remote->{'userid'}, 'M');
}
$ret .= "<?h1 $ML{'.success'} h1?>";
if ($mode eq 'create') {
$ret .= "<?p $ML{'.label.commcreated'} p?>";
} else {
$ret .= "<?p $ML{'.label.commchanged'} p?>";
}
$ret .= "<?p $ML{'.label.rellinks'} <ul><li><a href='$LJ::SITEROOT/community/$cu->{'user'}/'>$ML{'.label.commsite'}</a></li>";
$ret .= "<li><a href='/userinfo.bml?user=$cu->{'user'}'>$ML{'.label.comminfo'}</a></li><li>"
. BML::ml('.label.managepage', { 'aopts' => 'href="/community/manage.bml"' }) . "</li></ul> p?>";
return $ret;
}
}
# we're either creating a new community or modifying settings of an existing one
# based on whether $mode is 'create' or 'modify'. Most of the page is the same in
# either case, and additionally we must preload existing settings when modifying.
my ($cname, $c);
$cname = $POST{'cuser'}; # if we're falling through with errors when creating
my %info = (
'membership'=>$POST{'membership'} || 'open',
'postlevel'=>$POST{'postlevel'} || 'members',
'nonmember_posting'=>$POST{'nonmember_posting'} || 0,
'moderated'=>$POST{'moderated'} || 0,
);
if ($mode eq 'modify') {
$cname = LJ::canonical_username($GET{'comm'});
$c = LJ::load_user($cname);
unless ($c) {
# if no community was specified, redirect to manage.bml
return BML::redirect("$LJ::SITEROOT/community/manage.bml");
}
unless ($c->{'journaltype'} eq 'C') {
$ret = "<?h1 $ML{'Error'} h1?><?p $ML{'.error.notcomm'} p?>";
return $ret;
}
my $dbr = LJ::get_db_reader();
($info{'membership'},$info{'postlevel'}) =
$dbr->selectrow_array("SELECT membership, postlevel FROM community WHERE userid=$c->{'userid'}");
LJ::load_user_props($c, "nonmember_posting", "moderated");
$info{'nonmember_posting'} = $c->{'nonmember_posting'} ? 1 : 0;
$info{'moderated'} = $c->{'moderated'} ? 1 : 0;
}
$ret .= "<form method='post' action='settings.bml?mode=$mode'>";
if ($mode eq 'modify') {
$ret .= "<?h1 $ML{'.label.changeheader'} h1?><?p $ML{'.label.changetext'} p?>";
} else {
$ret .= "<?h1 $ML{'.label.createheader'} h1?><?p $ML{'.label.createtext'} p?>";
}
if ($mode eq 'create') {
LJ::set_active_crumb('createcommunity');
$ret .= "<?h2 $ML{'.label.commheader'} h2?>" .
($mode eq 'modify' ? "<?p $ML{'.label.commchange'} p?>" : "<?p $ML{'.label.commcreate'} p?>");
$ret .= "<?standout <table width='350' cellpadding='7'><tr valign='top'><td><b>$ML{'.label.maintainer'}</b></td>";
$ret .= "<td><?ljuser $remote->{'user'} ljuser?><br />$ML{'.label.maintainer.login'}</td></tr>";
$ret .= "<tr valign='top'><td><b>$ML{'.label.community'}</b></td>";
$ret .= "<td>$ML{'.label.username'}<br /><input name='cuser' maxlength='15' value='$cname' /><br />";
$ret .= "<?inerr $errors{'username'} inerr?><br />";
$ret .= "$ML{'.label.password'}<br /><input name='cpassword' type='password' /><br />";
$ret .= "<?inerr $errors{'password'} inerr?></td></tr></table> standout?>";
} else {
LJ::set_active_crumb('commsettings');
$ret .= LJ::html_hidden('cuser', $cname);
$ret .= "<?p " . BML::ml('.name',{'name'=>"<?ljcomm $cname ljcomm?>"});
$ret .= " " . BML::ml('.members',{'link'=>"/community/members.bml?comm=$cname"}) . " p?>";
}
$ret .= "<?h1 $ML{'.label.commopts'} h1?><?p $ML{'.label.howoperates'} p?>";
$ret .= "<?h2 $ML{'.label.membership'} h2?><?p $ML{'.label.whocanjoin'} p?><div class='opts'>";
# membership levels
$ret .= "<p>";
$ret .= LJ::html_check({ type => 'radio', name => 'membership', id => 'memopen',
value => 'open', selected => ($info{membership} eq 'open' ? 1 : 0)});
$ret .= "<label for='memopen' $ML{'.label.openmemb'}</label></p><p>";
$ret .= LJ::html_check({ type => 'radio', name => 'membership', id => 'memmoderated',
value => 'moderated', selected => ($info{membership} eq 'moderated' ? 1 : 0)});
$ret .= "<label for='memmoderated' $ML{'.label.moderatedmemb'}</label></p><p>";
$ret .= LJ::html_check({ type => 'radio', name => 'membership', id => 'memclosed',
value => 'closed', selected => ($info{membership} eq 'closed' ? 1 : 0)});
$ret .= "<label for='memclosed' $ML{'.label.closedmemb2'}</label></p>";
$ret .= "</div>";
my ($optopen,$optclosed);
if ($info{'postlevel'} eq 'members') {
($optopen,$optclosed)=(" checked='checked'","");
} else {
($optopen,$optclosed)=("", " checked='checked'");
}
$ret .= "<?h2 $ML{'.label.postaccess'} h2?><?p $ML{'.label.whocanpost'} p?><div class='opts'>";
$ret .= "<input type='radio' id='postopen' name='postlevel' value='members'$optopen /><label for='postopen'> $ML{'.label.anybodycan'}</label>";
$ret .= "<p><input type='radio' id='postclosed' name='postlevel' value='select'$optclosed /><label for='postclosed'> $ML{'.label.selcan'}</label>";
$ret .= "</div>";
if ($info{'nonmember_posting'}) {
($optopen,$optclosed)=(" checked='checked'","");
} else {
($optopen,$optclosed)=("", " checked='checked'");
}
$ret .= "<?h2 $ML{'.label.nmheader'} h2?><?p $ML{'.label.nmtext'} p?><div class='opts'>";
$ret .= "<input type='radio' id='nonopen' name='nonmember_posting' value='0'$optclosed /><label for='nonopen'> $ML{'.label.nmcant'}</label>";
$ret .= "<p><input type='radio' id='nonclosed' name='nonmember_posting' value='1'$optopen /><label for='nonclosed'> $ML{'.label.nmcan'}</label>";
$ret .= "</div>";
if ($info{'moderated'}) {
($optopen,$optclosed)=(" checked='checked'","");
} else {
($optopen,$optclosed)=("", " checked='checked'");
}
$ret .= "<?h2 $ML{'.label.modheader'} h2?><?p $ML{'.label.modtext'} p?><div class='opts'>";
$ret .= "<input type='radio' id='radunmod' name='moderated' value='0'$optclosed /><label for='radunmod'> $ML{'.label.modisnt'}</label>";
$ret .= "<p><input type='radio' id='radmod' name='moderated' value='1'$optopen /><label for='radmod'> $ML{'.label.modis'}</label>";
$ret .= "</div>\n";
$ret .= "<center><input type='submit' value='" .
($mode eq 'create' ? "$ML{'.button.createcommunity'}" : "$ML{'.button.changecommunity'}") .
"' /></center></form>";
return $ret;
_code?>
<=body
page?>

View File

@@ -0,0 +1,66 @@
<?_code
{
LJ::set_active_crumb('transfercomm');
$title = $ML{'.title'};
$body = "";
# is there a user out there?
my $remote = LJ::get_remote();
return "<?needlogin?>" unless $remote;
# bad statusvis?
unless ($remote->{statusvis} eq 'V') {
$body = "<?h1 $ML{'.badstatus.title'} h1?><?p $ML{'.badstatus.body'} p?>";
return;
}
# see if they posted and perform actions if so
if (LJ::did_post()) {
# get info about the community
my $cu = LJ::load_user($POST{comm});
$errors{username} = $ML{'.error.notfound'} unless $cu;
# the rest of the errors assume a community exists
if ($cu) {
# status/type errors
$errors{username} = $ML{'.error.notcomm'} unless $cu->{journaltype} eq 'C';
$errors{username} = $ML{'.error.badstatus'} if !%errors && $cu->{statusvis} !~ /[VD]/;
# are they already a maintainer?
$errors{username} = $ML{'.error.alreadyadmin'} if !%errors && LJ::can_manage($remote, $cu);
# check the password last
$errors{password} = $ML{'.error.nopassword'} if !%errors && !$POST{password};
$errors{password} = $ML{'.error.mismatch'} if !%errors && !LJ::auth_okay($cu, $POST{password});
}
# everything checks out, transfer it
unless (%errors) {
$cu->log_event('maintainer_add', { actiontarget => $remote->{userid}, remote => $remote });
LJ::set_rel($cu, $remote, 'A');
$body = "<?h1 $ML{'.success.title'} h1?><?p ";
$body .= BML::ml('.success.body', { comm => LJ::ljuser($cu, { type => 'C' }) });
$body .= " p?>";
return;
}
}
# setup the form to transfer the community
$body .= "<?h1 $ML{'.title'} h1?><?p $ML{'.body'} p?>";
$body .= "<form method='post' action='/community/transfer.bml'>";
$body .= "<table><tr><td>$ML{'.account'}</td>";
$body .= "<td>" . LJ::html_text({ name => 'comm', id => 'comm', value => $POST{comm} }) . "</td></tr>\n";
$body .= "<tr><td></td><td><?inerr $errors{username} inerr?></td></tr>" if $errors{username};
$body .= "<tr><td>$ML{'.password'}</td>";
$body .= "<td>" . LJ::html_text({ type => 'password', name => 'password', id => 'password' }) . "</td></tr>\n";
$body .= "<tr><td></td><td><?inerr $errors{password} inerr?></td></tr>" if $errors{password};
$body .= "<tr><td></td><td>" . LJ::html_submit(undef, $ML{'.button.title'}) . "</td></tr>";
$body .= "</table></form>";
return;
}
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?>

620
livejournal/htdocs/create.bml Executable file
View File

@@ -0,0 +1,620 @@
<?page
title=><?_ml .title _ml?>
head<=
<style type="text/css">
.tablecontent {
border-top: 1px solid #dfdfdf;
border-bottom: 1px solid #dfdfdf;
padding-top: 5px;
padding-bottom: 5px;
text-align: center;
width: 8%;
}
.tablelabel {
border-top: 1px solid #dfdfdf;
border-bottom: 1px solid #dfdfdf;
padding-top: 5px;
padding-bottom: 5px;
width: 20%;
font-size: .9em;
}
.tablehead {
border-bottom: 1px solid #dfdfdf;
padding-top: 5px;
padding-bottom: 5px;
font-weight: bold;
white-space: nowrap;
text-align: center;
}
.tablebottom {
border-top: 1px solid #dfdfdf;
padding-top: 5px;
white-space: nowrap;
}
</style>
<=head
body<=
<?_code
my $crumb = $LJ::IS_SSL ? 'securecreatejournal' : 'createjournal';
LJ::set_active_crumb($crumb);
return LJ::server_down_html() if ($LJ::SERVER_DOWN);
return "<?badinput?>" unless LJ::text_in(\%POST);
my $mode = $POST{'mode'};
my $code = $POST{'code'} || $GET{'code'};
if ($LJ::USE_SSL && ! $LJ::IS_SSL && $FORM{'ssl'} ne "no") {
return BML::redirect("$LJ::SSLROOT/create.bml");
}
# with no mode, decide which screen the user sees first, based
# on whether or not this LJ installation lets in free users
if ($mode eq "") {
$mode = $LJ::USE_ACCT_CODES ?
($code ? "codesubmit" : "entercode")
: "getinfo";
}
my $remote = LJ::get_remote();
my %errors;
my $error_msg = sub {
my $key = shift;
my $pre = shift;
my $post = shift;
my $msg = $errors{$key};
return unless $msg;
return "$pre $msg $post";
};
# Flag to indicate they've submitted with 'audio' as the answer to the spambot
# challenge.
my $wants_audio = 0;
# Captcha id
my ($capid, $anum);
# validate a code they've entered and throw them back to entercode
# mode if it's invalid
if ($code && $mode eq "submit" || # account codes turned off, but one specified anyway
$LJ::USE_ACCT_CODES && ($mode eq "codesubmit" || $mode eq "submit")) # account codes required
{
my $error;
my $userid = 0; # acceptable userid for double-click protection
if ($mode eq "submit") {
my $u = LJ::load_user($POST{'user'});
$userid = $u->{'userid'};
}
$errors{'code'} = $error
unless (LJ::acct_code_check($code, \$error, $userid));
if (%errors) {
$mode = "entercode";
} elsif ($mode eq "codesubmit") {
$mode = "getinfo";
}
}
# MODE: entercode - enter an account code to proceed making an account
if ($LJ::USE_ACCT_CODES && $mode eq "entercode")
{
my $ret;
my $v;
$ret .= "<form method=\"post\" action=\"create.bml\">\n";
$ret .= LJ::html_hidden(mode => 'codesubmit',
ssl => $FORM{'ssl'});
$ret .= "<?h1 $ML{'.useacctcodes.welcome'} h1?><?p $ML{'.useacctcodes.entercode'} p?>";
$v = LJ::ehtml($code);
$ret .= "<?standout Code: <input type=\"text\" name=\"code\" value=\"$v\" size=\"13\" maxlength=\"12\"> <input type=\"submit\" value=\"$ML{'.btn.proceed'}\">";
$ret .= $error_msg->('code', '<br>');
$ret .= " standout?>";
$ret .= "</form>\n";
open (REM, "$LJ::HOME/htdocs/inc/account-codes");
while (<REM>) {
$ret .= $_;
}
close REM;
return $ret;
}
# MODE: submit - if they've given 'audio' as the answer to the spambot-blocker,
# reset the mode to 'getinfo' and set the audio flag
if ( $LJ::HUMAN_CHECK{create} && $mode eq 'submit' && lc($POST{answer}) eq 'audio' )
{
$mode = 'getinfo';
$wants_audio = 1;
}
# MODE: submit - try to create an account. might change mode
# if there are errors, we'll populate %errors and
# return to "getinfo" mode below
SUBMIT:
while ($mode eq "submit") # using while instead of if so we can 'last' out of it
{
return "<b>$ML{'Error'}</b>: $ML{'.error.postrequired'}" unless LJ::did_post();
my $user = LJ::canonical_username($POST{'user'});
my $email = LJ::trim(lc($POST{'email'}));
# setup global things that can be used to modify the user later
my $is_underage = 0; # turn on if the user should be marked as underage
my $ofage = 0; # turn on to note that the user is over 13 in actuality
# (but is_underage might be on which just means that their
# account is being marked as underage--even if they're old
# enough [unique cookie check])
# reject this email?
return LJ::sysban_block(0, "Create user blocked based on email",
{ 'new_user' => $user, 'email' => $email, 'name' => $user })
if LJ::sysban_check('email', $email);
my $dbh = LJ::get_db_writer();
if (length($user) > 15) {
$errors{'username'} = "$ML{'error.usernamelong'}";
}
if ($POST{'user'} && ! $user) {
$errors{'username'} = "$ML{'error.usernameinvalid'}";
}
unless ($POST{'user'}) {
$errors{'username'} = "$ML{'.error.username.mustenter'}";
}
foreach my $re ("^system\$", @LJ::PROTECTED_USERNAMES) {
next unless ($user =~ /$re/);
# you can give people sharedjournal priv ahead of time to create
# reserved communities:
next if LJ::check_priv($remote, "sharedjournal", $user);
$errors{'username'} = "$ML{'.error.username.reserved'}";
}
# see if they're confused and entered a valid account code
# for their username (happens often)
if ($LJ::USE_ACCT_CODES && $user =~ /^.....a[ab].....$/) {
# see if the acctcode is valid and unused
my ($acid, $auth) = LJ::acct_code_decode($user);
my $is_valid = $dbh->selectrow_array("SELECT COUNT(*) FROM acctcode ".
"WHERE acid=? AND rcptid=0",
undef, $acid);
$errors{'username'} = "$ML{'.error.username.iscode'}"
if $is_valid;
}
my $u = LJ::load_user($user);
my $second_submit = 0;
if ($u) {
my $in_use = 1;
if ($u->{'email'} eq $POST{'email'}) {
if (LJ::login_ip_banned($u)) {
# brute-force possible going on
} else {
if ($u->{'password'} eq $POST{'password1'}) {
# oh, they double-clicked the submit button
$second_submit = 1;
$in_use = 0;
} else {
LJ::handle_bad_login($u);
}
}
}
if ($in_use) {
$errors{'username'} = "$ML{'.error.username.inuse'}";
}
}
$POST{'password1'} = LJ::trim($POST{'password1'});
$POST{'password2'} = LJ::trim($POST{'password2'});
if ($POST{'password1'} ne $POST{'password2'}) {
$errors{'password'} = "$ML{'.error.password.nomatch'}";
} else {
my @checkpass = LJ::run_hooks("bad_password",
{ 'user' => $user, 'name' => $user,
'email' => $email, 'password' => $POST{'password1'} });
if (@checkpass && $checkpass[0]->[0]) {
$errors{'password'} = "Bad password: $checkpass[0]->[0]";
}
}
if (! $POST{'password1'}) {
$errors{'password'} = "$ML{'.error.password.blank'}";
} elsif (length $POST{'password1'} > 30) {
$errors{'password'} = "$ML{'password.max30'}";
}
unless (LJ::is_ascii($POST{'password1'})) {
$errors{'password'} = "$ML{'.error.password.asciionly'}";
}
### start COPPA_CHECK
# age checking to determine how old they are
if ($LJ::COPPA_CHECK) {
my $uniq;
if ($LJ::UNIQ_COOKIES) {
$uniq = Apache->request->notes('uniq');
if ($uniq) {
my $timeof = $dbh->selectrow_array('SELECT timeof FROM underage WHERE uniq = ?', undef, $uniq);
$is_underage = 1 if $timeof && $timeof > 0;
}
}
my ($year, $mon, $day) = ( $POST{"bday_yyyy"}+0, $POST{"bday_mm"}+0, $POST{"bday_dd"}+0 );
if ($year < 100) {
$POST{'bday_yyyy'} += 1900;
$year += 1900;
}
# get current time
my ($nday, $nmon, $nyear) = (gmtime())[3, 4, 5];
$nyear += 1900;
$nmon += 1;
# require dates in the 1900s (or beyond)
if ($year && $mon && $day && $year >= 1900 && $year <= $nyear) {
# now see how many years back they are
my $ofageyear = $year + 13;
if ($ofageyear > $nyear) {
$is_underage = 1;
} elsif ($ofageyear == $nyear) {
# years match, see if they were born after this month
if ($mon > $nmon) {
$is_underage = 1;
} elsif ($mon == $nmon) {
# now check the day
if ($day > $nday) {
$is_underage = 1;
} else {
$ofage = 1;
}
} else {
$ofage = 1;
}
} else {
$ofage = 1;
}
} else {
$errors{'bday'} = "$ML{'.error.birthday.invalid'}";
}
# note this unique cookie as underage (if we have a unique cookie)
if ($is_underage && $uniq) {
$dbh->do("REPLACE INTO underage (uniq, timeof) VALUES (?, UNIX_TIMESTAMP())", undef, $uniq);
}
}
### end COPPA_CHECK
if ($LJ::TOS_CHECK && ! $POST{'agree_tos'}) {
$errors{'agree_tos'} = $ML{'tos.error'};
}
# check the email address
{
my @email_errors;
LJ::check_email($email, \@email_errors);
if ($LJ::USER_EMAIL and $email =~ /\@\Q$LJ::USER_DOMAIN\E$/i) {
push @email_errors, BML::ml(".error.email.lj_domain",
{domain => $LJ::USER_DOMAIN});
}
$errors{'email'} = join(", ", @email_errors) if @email_errors;
}
# Check the turing test answer if it's turned on
if ($LJ::HUMAN_CHECK{create}) {
($capid, $anum) = LJ::Captcha::session_check_code($POST{captcha_chal}, $POST{answer});
$errors{'captcha'} = $ML{'.captcha.invalid'} unless $capid && $anum;
}
last SUBMIT if %errors;
my $clusterid = ($LJ::ALLOW_CLUSTER_SELECT
? $POST{'cluster_id'}
: LJ::new_account_cluster()) + 0;
die "Cluster 0 not supported" unless $clusterid;
my $userid = $u ? $u->{'userid'}+0 : 0;
unless ($second_submit)
{
my $caps = int($LJ::NEWUSER_CAPS);
my $status = ($LJ::EVERYONE_VALID ? 'A' : 'N');
$dbh->do("INSERT INTO user (user, email, password, status, caps, name, clusterid, dversion) ".
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
undef, $user, $email, $POST{'password1'}, $status, $caps,
$user, $clusterid, $LJ::MAX_DVERSION);
if ($dbh->err) {
return "<?h1 $ML{'Error'} h1?><?p $ML{'error.procrequest'} <b>" . $dbh->errstr . "</b> p?>";
}
$userid = $dbh->{'mysql_insertid'};
$dbh->do("REPLACE INTO useridmap (userid, user) VALUES (?, ?)", undef, $userid, $user);
$dbh->do("REPLACE INTO userusage (userid, timecreate) VALUES (?, NOW())", undef, $userid);
# if we're using account codes on this site, mark the code as used
if ($code) {
my ($acid, $auth) = LJ::acct_code_decode($code);
$dbh->do("UPDATE acctcode SET rcptid=$userid WHERE acid=$acid");
if ($dbh->err) { return $dbh->errstr; }
}
# if we have initial friends for new accounts, add them.
foreach my $friend (@LJ::INITIAL_FRIENDS) {
my $friendid = LJ::get_userid($friend);
LJ::add_friend($userid, $friendid) if $friendid;
}
foreach my $friend (@LJ::INITIAL_OPTIONAL_FRIENDS) {
my $friendid = LJ::get_userid($friend);
LJ::add_friend($userid, $friendid) if $friendid and $POST{"initial_optional_friend_$friend"};
}
# Set any properties that get set in new users
while (my ($name, $val) = each %LJ::USERPROP_INIT) {
LJ::set_userprop($userid, $name, $val);
}
# Mark the turing test for deletion
if ($LJ::HUMAN_CHECK{create}) {
LJ::Captcha::expire($capid, $anum, $userid);
}
LJ::run_hooks("post_create", {
'userid' => $userid,
'user' => $user,
'code' => $code,
});
}
# send welcome mail... unless they're underage
unless ($is_underage) {
my $aa = {};
if ($userid) {
$aa = LJ::register_authaction($userid, "validateemail", $email);
}
my $body = BML::ml('email.newacct2.body', {
"email" => $email,
"regurl" => "$LJ::SITEROOT/confirm/$aa->{'aaid'}.$aa->{'authcode'}",
"username" => $user,
"sitename" => $LJ::SITENAME,
"siteroot" => $LJ::SITEROOT,
"admin_email" => $LJ::ADMIN_EMAIL,
"bogus_email" => $LJ::BOGUS_EMAIL,
});
LJ::send_mail({
'to' => $email,
'from' => $LJ::ADMIN_EMAIL,
'fromname' => $LJ::SITENAME,
'charset' => 'utf-8',
'subject' => BML::ml('email.newacct.subject', {'sitename' => $LJ::SITENAME}),
'body' => $body,
});
}
my $nu = LJ::load_userid($userid, "force");
# now flag as underage (and set O to mean was old or Y to mean was young)
$nu->underage(1, $ofage ? 'O' : 'Y', 'account creation') if $is_underage;
if ($LJ::TOS_CHECK) {
my $err = "";
$nu->tosagree_set(\$err)
or return LJ::bad_input($err);
}
# record create information
$nu->log_event('account_create', { remote => $remote });
$nu->make_login_session;
# local sites may want to override what happens at this point
my $redirect = undef;
my $stop_output;
LJ::run_hooks("create.bml_postsession", {
post => \%POST,
u => $nu,
redirect => \$redirect,
ret => \$ret,
stop_output => \$stop_output,
});
return BML::redirect($redirect) if $redirect;
return $ret if $stop_output;
$ret = "<?h1 $ML{'.success.head'} h1?><?p ".BML::ml(".success.text1", {'email' => $email, 'username' => $user}) ." p?>";
my $uri = LJ::journal_base($nu);
$ret .= "<?p $ML{'.success.text2'} p?>\n";
$ret .= "<?standout <font size='+1' face='arial'><b><a href='$uri'>$uri/</a></b></font> standout?>\n";
$ret .= "<?p $ML{'.success.text3'} p?>\n";
$ret .= "<form method='get' action='$LJ::SITEROOT/editinfo.bml?authas=$user'>";
$ret .= "<p align='center'>" . LJ::html_submit(undef, "$ML{'.success.btn.enterinfo'} &rarr;") . "</p>";
$ret .= "</form>\n";
return $ret;
}
if ($mode eq "getinfo" || %errors)
{
my $ret;
my $v;
if (%errors) {
my @errors_order = ('code', 'username', 'email', 'password', 'agree_tos', 'captcha');
my %errors_def;
$errors_def{$_} = 1 for @errors_order;
foreach my $key (keys %errors) { push @errors_order, $key unless $errors_def{$key}; }
$ret .= "<?standout <strong>$ML{'.errors.label'}</strong><ul><li>";
$ret .= join ("</li><li>", grep { $_ } map { $errors{$_} } @errors_order);
$ret .= "</li></ul> standout?>";
}
$ret .= "<?p $ML{'.create.text'} p?>" unless %errors;
$ret .= "<form action=\"create.bml\" method=\"post\">\n";
$ret .= LJ::html_hidden(mode => 'submit',
code => $code,
ssl => $FORM{'ssl'});
$ret .= "<ol>";
### username
$v = LJ::ehtml($FORM{'user'});
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.username.box.head'}</div>";
$ret .= $error_msg->('username', '<p class="formitemFlag">', '</p>');
$ret .= "<div class='formitemDesc'>" . BML::ml(".username.text", {'sitename' => $LJ::SITENAME}) . "</div>";
$ret .= LJ::html_text({'name' => 'user', 'size' => 15, 'maxlength' => 15, 'value' => $v, raw => 'style="<?loginboxstyle?>"' });
$ret .= "<br />" . BML::ml('.community', { aopts => "href='$LJ::SITEROOT/community/create.bml'" });
$ret .= "<div class='formitemNote'>$ML{'.username.charsallowed'}</div>" if (!%errors || exists $errors{'username'});
$ret .= "</div></li>";
### email address
$v = LJ::ehtml($FORM{'email'});
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.email.input.head'}</div>";
$ret .= $error_msg->('email', '<p class="formitemFlag">', '</p>');
$ret .= "<div class='formitemDesc'>" . BML::ml('.email.text3', {
aopts => "target='_new' href='$LJ::SITEROOT/legal/privacy.bml'",
}) . "</div>";
$ret .= LJ::html_text({'name' => 'email', 'size' => 40, 'maxlength' => 50, 'value' => $v,});
$ret .= "</div></li>";
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.password.input.head1'}</div>";
$ret .= $error_msg->('password', '<p class="formitemFlag">', '</p>');
$ret .= "<div class='formitemFlag'>$ML{'.password.secure'}</div>" if exists $errors{'password'};
$ret .= "<div class='formitemDesc'>$ML{'.password.text'}</div>";
my $pass_value = $errors{'password'} ? "" : $POST{'password1'};
$ret .= LJ::html_text({'name' => 'password1', 'size' => 30, 'maxlength' => 31, 'type' => "password",
value => $pass_value, });
$ret .= "<div class='formitemDesc'>$ML{'.password.input.head2'}</div>";
$ret .= LJ::html_text({'name' => 'password2', 'size' => 30, 'maxlength' => 31, 'type' => "password",
value => $pass_value, });
$ret .= "</div></li>";
if (@LJ::INITIAL_OPTIONAL_FRIENDS) {
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.initialfriends.heading'}</div>";
$ret .= "<div class='formitemDesc'>$ML{'.initialfriends'}</div>";
$ret .= "<div>";
foreach my $friend (@LJ::INITIAL_OPTIONAL_FRIENDS) {
$ret .= LJ::html_check({'name' => "initial_optional_friend_$friend",
'value' => 1,
'selected' => $POST{"initial_optional_friend_$friend"},
'id' => "optfriend_$friend",
});
$ret .= "<label for='optfriend_$friend'>" .
LJ::ljuser($friend) . " " . $ML{".initial.friend.$friend"} .
"</label><br />";
}
$ret .= "</div></div></li>";
}
if ($LJ::COPPA_CHECK)
{
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.birthday.head'}</div>";
$ret .= "<div class='formitemFlag'>$errors{'bday'}</div>" if exists $errors{'bday'};
$ret .= "<div class='formitemDesc'>$ML{'.birthday.question'}</div><div>";
$ret .= "<table><tr><td><span style='font-weight: bold;'>$ML{'.birthday.birthdate'}</span></td><td>";
$ret .= LJ::html_datetime({ name => 'bday', notime => 1,
default => sprintf("%04d-%02d-%02d", $POST{bday_yyyy}, $POST{bday_mm}, $POST{bday_dd}) });
$ret .= "</td><td><span style='font-style: italic;'>$ML{'.birthday.required'}</span></td></tr>";
$ret .= "</table></div></div></li>";
}
LJ::run_hooks("create.bml_opts", {
post => \%POST,
get => \%GET,
ret => \$ret,
});
if ($LJ::TOS_CHECK)
{
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.tos.heading'}</div>";
$ret .= LJ::tosagree_widget($POST{agree_tos}, $errors->{agree_tos});
$ret .= "</div></li>";
}
if ($LJ::ALLOW_CLUSTER_SELECT) {
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.clusterselect.head'}</div>";
$ret .= "<div class='formitemDesc'>$ML{'.clusterselect.text'}</div>";
$ret .= LJ::html_select({ 'name' => 'cluster_id' },
"0", "$BML{'.clusterselect.nocluster'}",
map { $_, BML::ml(".clusterselect.clusternum", {'number' => $_}) } @LJ::CLUSTERS);
$ret .= "<div class='formitemNote'>$ML{'.clusterselect.cluster'}</div>";
$ret .= "</div></li>";
}
if ($LJ::HUMAN_CHECK{create}) {
my ($captcha_chal, $captcha_sess);
$captcha_chal = $POST{captcha_chal} || LJ::challenge_generate(900);
$captcha_sess = LJ::get_challenge_attributes($captcha_chal);
my $answer = $POST{answer};
undef $answer if $errors{'captcha'} || $wants_audio;
my $try = 0;
if ($form->{captcha_chal}) {
my $dbcm = LJ::get_cluster_reader();
$try = $dbcm->selectrow_array('SELECT trynum FROM captcha_session ' .
'WHERE sess=?', undef, $captcha_sess);
}
$ret .= "<li><div class='formitem'><div class='formitemName'>$ML{'.captcha.prove'}</div>";
# Visual challenge
unless ( $wants_audio || $POST{audio_chal} ) {
$ret .= "<div class='formitemDesc'>$ML{'.captcha.desc'}</div>";
if ($capid && $anum) { # previously entered correctly
$ret .= "<img src='/captcha/image.bml?capid=$capid&amp;anum=$anum' width='175' height='35' />";
} else {
$ret .= "<img src='/captcha/image.bml?chal=$captcha_chal&amp;try=$try' width='175' height='35' />";
}
}
# Audio challenge
else {
$ret .= "<div class='formitemDesc'>$ML{'.captcha.audiodesc'}</div>";
if ($capid && $anum) {
$ret .= "<a href='/captcha/audio.bml?capid=$capid&amp;anum=$anum'>$ML{'.captcha.play'}</a>";
} else {
$ret .= "<a href='/captcha/audio.bml?chal=$captcha_chal&amp;try=$try'>$ML{'.captcha.play'}</a>";
}
$ret .= LJ::html_hidden(audio_chal => 1);
}
$ret .= "<br /><br />$ML{'.captcha.answer'}";
$ret .= LJ::html_text({ name => 'answer', size => 15, value => $answer });
$ret .= LJ::html_hidden(captcha_chal => $captcha_chal);
$ret .= $error_msg->('captcha', '<p class="formitemFlag">', '</p>');
$ret .= "</div></li>";
}
$ret .= "</ol>";
$ret .= "<div style='width:600; text-align: center'>";
$ret .= "<input type=\"submit\" value=\"$ML{'.btn.create'}\">";
$ret .= "</div>";
$ret .= "</form>";
return $ret;
}
return "$ML{'error.unknownmode'}: <b>$mode</b>";
_code?>
<=body
page?><?_c <LJDEP>
link: htdocs/legal/privacy.bml
post: htdocs/create.bml, htdocs/editinfo.bml
file: htdocs/inc/account-codes
hook: post_create
</LJDEP> _c?>

View File

@@ -0,0 +1,29 @@
<?page
title=>Advanced Customization
body<=
<?_code
LJ::set_active_crumb('advcustomize');
return;
_code?>
<?h1 Disclaimer h1?>
<?p
Unless you're a programmer or web designer, most users should stay away from this area and use
the <a href="/customize/">main customization area</a>, which is designed for anybody to use.
p?>
<?h1 Documentation h1?>
<?p Before you jump into tweaking/programming S2, you should understand how it works. p?>
<ul>
<li><a href="/doc/s2/">S2 Documentation</a> -- under construction, but some good content</li>
<li><a href="layerbrowse.bml">Public Layers</a> -- system layers (good reference &amp; place to learn)</li>
</ul>
<?h1 Advanced Options h1?>
<ul>
<li><a href="layers.bml">Your Layers</a> -- create/manage your layers</li>
<li><a href="styles.bml">Your Styles</a> -- create/manage your styles</li>
</ul>
<=body
page?>

View File

@@ -0,0 +1,308 @@
<?_code # -*-bml-*-
{
use strict;
use vars qw(%GET %POST $title $body);
LJ::set_active_crumb('layerbrowse');
# start of content
$body = BML::ml("Backlink", {
'link' => './',
'text' => 'Advanced Customization',
});
my $err = sub {
$title = "Error";
$body = shift;
return;
};
my $pub = LJ::S2::get_public_layers();
my $id;
if ($GET{'id'} =~ /^\d+$/) { # numeric
$id = $GET{'id'};
} elsif ($GET{'id'}) { # redist_uniq
$id = $pub->{$GET{'id'}}->{'s2lid'};
}
my $dbr = LJ::get_db_reader();
my $remote = LJ::get_remote();
# show the public layers
unless ($id) {
$title = "Public Layers";
my %layerinfo;
my @to_load = grep { /^\d+$/ } keys %$pub;
LJ::S2::load_layer_info(\%layerinfo, \@to_load);
my $recurse = sub {
my $self = shift;
my $lid = shift; # layer id
my $lay = $pub->{$lid};
return unless $lay;
# set to true if the layer is not core and is not a layout
my $is_child = $lay->{'type'} ne 'core' && $lay->{'type'} ne 'layout';
my $typedes = " ($lay->{'type'}" . (! $is_child ? ": <b>$lid</b>" : '') . ")";
# show link to detailed view
$body .= "<li><a href='layerbrowse.bml?id=$lay->{'uniq'}'>" . LJ::ehtml($layerinfo{$lid}->{'name'});
$body .= "</a>$typedes</li>";
# done unless there are children to recurse through
return unless ! $is_child && $lay->{'children'};
# if we're not expanding these children, stop and show a link
if ($lay->{'type'} eq 'layout' && $GET{'expand'} != $lid) {
$body .= "<ul><li>[<a href='layerbrowse.bml?expand=$lid'>";
$body .= scalar(@{$lay->{'children'}}) . " children...</a>]</li></ul>";
return;
}
# expand children
$body .= "<ul>";
foreach (@{$lay->{'children'}}) {
$self->($self, $_);
}
$body .= "</ul>";
return;
};
# iterate through core layers
$body .= "<ul>";
foreach (grep { $pub->{$_}->{'b2lid'} == 0 } grep { /^\d+$/ } keys %$pub) {
$recurse->($recurse, $_); # start from the top
}
$body .= "</ul>";
return;
}
### details on a specific layer ###
my $xlink = sub {
my $r = shift;
$$r =~ s/\[class\[(\w+)\]\]/<a href=\"\#class.$1\">$1<\/a>/g;
$$r =~ s/\[method\[(.+?)\]\]/<a href=\"\#meth.$1\">$1<\/a>/g;
$$r =~ s/\[function\[(.+?)\]\]/<a href=\"\#func.$1\">$1<\/a>/g;
$$r =~ s/\[member\[(.+?)\]\]/<a href=\"\#member.$1\">$1<\/a>/g;
};
# load layer info
my $layer = defined $pub->{$id} ? $pub->{$id} : LJ::S2::load_layer($id);
return $err->("The specified layer does not exist.")
unless $layer;
my $layerinfo = {};
LJ::S2::load_layer_info($layerinfo, [ $id ]);
my $srcview = exists $layerinfo->{$id}->{'source_viewable'} ?
$layerinfo->{$id}->{'source_viewable'} : undef;
# do they have access?
my $isadmin = !defined $pub->{$id} && # public styles are pulled from the system
(LJ::check_priv($remote, 'canview', 'styles') || # account, so we don't want to check privileges
LJ::check_priv($remote, 'canview', '*')); # in case they're private styles
return $err->("You are not authorized to view this layer.")
unless defined $pub->{$id} || $srcview == 1 ||
LJ::can_manage($remote, $layer->{'userid'}) ||
$isadmin;
LJ::S2::load_layers($id);
my $s2info = S2::get_layer_all($id);
my $class = $s2info->{'class'} || {};
my $xlink_args = sub {
my $r = shift;
return unless
$$r =~ /^(.+?\()(.*)\)$/;
my ($new, @args) = ($1, split(/\s*\,\s*/, $2));
foreach (@args) {
s/^(\w+)/defined $class->{$1} ? "[class[$1]]" : $1/eg;
}
$new .= join(", ", @args) . ")";
$$r = $new;
$xlink->($r);
};
$body .= "<br />";
# link to layer list if this is a public layer, otherwise user's layer list
if (defined $pub->{$id}) {
$body .= BML::ml('backlink', { 'link' => 'layerbrowse.bml', 'text' => 'Public Layers' }) . "\n";
} else {
$body .= BML::ml('backlink', { 'link' => "layers.bml", 'text' => 'Your Layers' }) . "\n";
$body .= BML::ml('actionlink', { 'link' => "<a href='layeredit.bml?id=$id'>Edit Layer</a>" }) . "\n";
}
if ($layer->{'b2lid'}) {
$body .= "[<a href=\"layerbrowse.bml?id=$layer->{'b2lid'}\">Parent Layer</a>]\n";
}
if (defined $pub->{$id} && (! defined $srcview || $srcview != 0) ||
$srcview == 1 ||
LJ::can_manage($remote, $layer->{'userid'}) ||
$isadmin) {
$body .= "[<a href=\"layersource.bml?id=$id\">Download</a>]\n";
$body .= "[<a href=\"layersource.bml?id=$id&fmt=html\">View as HTML</a>]\n";
}
# layerinfo
if (my $info = $s2info->{'info'}) {
$body .= "<?h1 Layer Info h1?>";
$body .= "<table style='margin-bottom: 10px' border='1' cellpadding='2'>";
foreach my $k (sort keys %$info) {
my ($ek, $ev) = map { LJ::ehtml($_) } ($k, $info->{$k});
$title = $ev if $k eq "name";
$body .= "<tr><td><b>$ek</b></td><td>$ev</td></tr>\n";
}
$body .= "</table>";
}
# sets
if (my $set = $s2info->{'set'}) {
$body .= "<?h1 Properties Set h1?>";
$body .= "<table style='margin-bottom: 10px' border='1' cellpadding='2'>";
foreach my $k (sort keys %$set) {
my $v = $set->{$k};
if (ref $v eq "HASH") {
if ($v->{'_type'} eq "Color") {
$v = "<span style=\"border: 1px solid #000000; padding-left: 2em; background-color: $v->{'as_string'}\">&nbsp;</span> <tt>$v->{'as_string'}</tt>";
} else {
$v = "[unknown object type]";
}
} elsif (ref $v eq "ARRAY") {
$v = "<i>List:</i> (" . join(", ", map { LJ::ehtml($_) } @$v) . ")";
} else {
$v = LJ::ehtml($v);
}
$body .= "<tr><td><b>$k</b></td><td>$v</td></tr>\n";
}
$body .= "</table>";
}
# global functions
my $gb = $s2info->{'global'};
if (ref $gb eq "HASH" && %$gb) {
$body .= "<?h1 Global Functions h1?>";
$body .= "<table style='margin-bottom: 10px' border='1' cellpadding='2'>";
foreach my $fname (sort keys %$gb) {
my $rt = $gb->{$fname}->{'returntype'};
if (defined $class->{$rt}) {
$rt = "[class[$rt]]";
}
$xlink->(\$rt);
my $ds = LJ::ehtml($gb->{$fname}->{'docstring'});
$xlink->(\$ds);
my $args = $gb->{$fname}->{'args'};
$xlink_args->(\$args);
$body .= "<tr><td><nobr><a name='func.$fname'><tt>$args : $rt</tt></a></nobr></td><td>$ds</td></tr>";
}
$body .= "</table>";
}
if (%$class)
{
# class index
$body .= "<?h1 Classes h1?>";
$body .= "<table style='margin-bottom: 10px'><tr valign='top' align='left'>";
$body .= "<td width='50%'>Alphabetical";
$body .= "<ul>";
foreach my $cname (sort { lc($a) cmp lc($b) } keys %$class) {
$body .= "<li><a href='#class.$cname'><b>$cname</b></a></li>\n";
}
$body .= "</ul>";
$body .= "</td>";
$body .= "<td width='50%'>Hierarchical";
my $dumpsub = sub {
my $self = shift;
my $parent = shift;
$body .= "<li><a href='#class.$parent'><b>$parent</b></a></li>\n"
if $parent;
my $didul = 0;
foreach my $cname (sort { lc($a) cmp lc($b) } keys %$class) {
next unless $class->{$cname}->{'parent'} eq $parent;
unless ($didul++) { $body .= "<ul>"; }
$self->($self, $cname);
}
if ($didul) { $body .= "</ul>"; }
};
$dumpsub->($dumpsub, "");
$body .= "</td></tr></table>";
# classes
foreach my $cname (sort { lc($a) cmp lc($b) } keys %$class) {
$body .= "<a name='class.$cname'><?h1 $cname Class h1?></a>";
my $ds = LJ::ehtml($class->{$cname}->{'docstring'});
if ($class->{$cname}->{'parent'}) {
$ds = "Child class of [class[$class->{$cname}->{'parent'}]]. $ds";
}
if ($ds) {
$xlink->(\$ds);
$body .= "<?p $ds p?>";
}
# build functions & methods
my (%func, %var);
my $add = sub {
my ($self, $aname) = @_;
foreach (keys %{$class->{$aname}->{'funcs'}}) {
$func{$_} = $class->{$aname}->{'funcs'}->{$_};
$func{$_}->{'_declclass'} = $aname;
}
foreach (keys %{$class->{$aname}->{'vars'}}) {
$var{$_} = $class->{$aname}->{'vars'}->{$_};
$var{$_}->{'_declclass'} = $aname;
}
my $parent = $class->{$aname}->{'parent'};
$self->($self, $parent) if $parent;
};
$add->($add, $cname);
$body .= "<table style='margin-bottom: 10px' border='1' cellpadding='2'><?h2 Members h2?>" if %var;
foreach (sort keys %var) {
my $type = $var{$_}->{'type'};
$type =~ s/(\w+)/defined $class->{$1} ? "[class[$1]]" : $1/eg;
$xlink->(\$type);
my $ds = LJ::ehtml($var{$_}->{'docstring'});
$xlink->(\$ds);
if ($var{$_}->{'readonly'}) {
$ds = "<i>(Read-only)</i> $ds";
}
$body .= "<tr><td><nobr><a name='member.${cname}.$_'><tt>$type $_</tt></a></nobr></td><td>$ds</td></tr>";
}
$body .= "</table>" if %var;
$body .= "<table style='margin-bottom: 10px' border='1' cellpadding='2'><?h2 Methods h2?>" if %func;
foreach (sort keys %func) {
my $rt = $func{$_}->{'returntype'};
if (defined $class->{$rt}) {
$rt = "[class[$rt]]";
}
$xlink->(\$rt);
my $ds = LJ::ehtml($func{$_}->{'docstring'});
$xlink->(\$ds);
my $args = $_;
$xlink_args->(\$args);
$body .= "<tr><td><nobr><a name='meth.${cname}::$_'><tt>$args : $rt</tt></a></nobr></td><td>$ds</td></tr>";
}
$body .= "</table>" if %func;
}
}
return;
}
_code?><?page title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?>

View File

@@ -0,0 +1,139 @@
<style>
em.error { font-weight: bold; color: red; font-style: normal; }
textarea.s2code { width: 100%; display: block; clear: both; padding: 2px; }
</style>
<?_code # -*-bml-*-
{
use strict;
use vars qw(%GET %POST);
# for error reporting
my $err = sub {
return "<title>Error</title>\n<h2>Error</h2>" . shift;
};
# we need a valid id
my $id = $GET{'id'} if $GET{'id'} =~ /^\d+$/;
return $err->("You have not specified a layer to edit.")
unless $id;
# authenticate user;
my $remote = LJ::get_remote();
return $err->("You must be logged in to edit layers.")
unless $remote;
# load layer
my $lay = LJ::S2::load_layer($id);
return $err->("The specified layer does not exist.")
unless $lay;
# if the b2lid of this layer has been remapped to a new layerid
# then update the b2lid mapping for this layer
my $b2lid = $lay->{b2lid};
if ($b2lid && $LJ::S2LID_REMAP{$b2lid}) {
LJ::S2::b2lid_remap($remote, $id, $b2lid);
$lay->{b2lid} = $LJ::S2LID_REMAP{$b2lid};
}
# is authorized admin for this layer?
return $err->('You are not authorized to edit this layer.')
unless LJ::can_manage($remote, $lay->{'userid'});
# get u of user they are acting as
my $u = $lay->{'userid'} == $remote->{'userid'} ? $remote : LJ::load_userid($lay->{'userid'});
# check priv and ownership
return $err->("You are not authorized to edit styles.")
unless LJ::get_cap($u, "s2styles");
# at this point, they are authorized, allow viewing & editing
my $ret;
$ret .= "<form method='post' action='layeredit.bml?id=$id'>\n";
$ret .= BML::ml('backlink', { 'text' => 'Your Layers', 'link' => "layers.bml?authas=$u->{'user'}" }) . "\n";
# get s2 code from db - use writer so we know it's up-to-date
my $dbh = LJ::get_db_writer();
my $s2code = $POST{'s2code'};
$s2code = $dbh->selectrow_array("SELECT s2code FROM s2source WHERE s2lid=?",
undef, $lay->{'s2lid'}) unless $s2code;
# we tried to compile something
if ($POST{'action'} eq "compile") {
$ret .= "<div style='margin: 20px 0 20px 40px'>\n";
$ret .= "<b>S2 Compiler Output</b> <em>at " . scalar(localtime) . "</em><br />\n";
my $error;
$POST{'s2code'} =~ s/\r//g; # just in case
unless (LJ::S2::layer_compile($lay, \$error, { 's2ref' => \$POST{'s2code'} })) {
$error =~ s/LJ::S2,.+//s;
$error =~ s!, .+?(src/s2|cgi-bin)/!, !g;
$ret .= "Error compiling layer:\n<pre style=\"border-left: 1px red solid\">$error</pre>";
# display error with helpful context
if ($error =~ /^compile error: line (\d+)/i) {
my $errline = $1;
my $kill = $errline - 5 < 0 ? 0 : $errline - 5;
my $prehilite = $errline - 1 > 4 ? 4: $errline - 1;
my $snippet = $s2code;
# make sure there's a newlilne at the end
chomp $snippet;
$snippet .= "\n";
# and now, fun with regular expressions
my $ct = 0;
$snippet =~ s!(.*?)\n!sprintf("%3d", ++$ct) . ": " .
$1 . "\n"!ge; # add line breaks and numbering
$snippet = LJ::ehtml($snippet);
$snippet =~ s!^((?:.*?\n){$kill,$kill}) # kill before relevant lines
((?:.*?\n){$prehilite,$prehilite}) # capture context before error
(.*?\n){0,1} # capture error
((?:.*?\n){0,4}) # capture context after error
.* # kill after relevant lines
!$2<em class='error'>$3</em>$4!sx;
$ret .= "<b>Context</b><br /><pre>$snippet</pre>\n";
}
} else {
$ret .= "No errors\n";
}
$ret .= "</div>\n\n";
}
$ret .= LJ::html_hidden("action", "compile") . "\n";
$ret .= "<p>" . LJ::html_submit('submit', 'Compile', {
'style' => 'float: right; margin-bottom: 2px',
'accesskey' => 'c',
'title' => 'alt-C: compile',
} ) . "\n";
$ret .= "<b>Edit layer source</b>\n";
$ret .= LJ::html_textarea({ 'name' => 's2code', 'class' => 's2code', 'wrap' => 'off',
'cols' => '50', 'rows' => '40', 'value' => $s2code }) . "\n";
$ret .= LJ::html_submit('submit', 'Compile') . "\n";
$ret .= "</p></form>\n";
# load layer info
my $layinf = {};
LJ::S2::load_layer_info($layinf, [ $id ]);
# find a title to display on this page
my $type = $layinf->{$id}->{'type'};
my $name = $layinf->{$id}->{'name'};
# find name of parent layer if this is a child layer
if (! $name && $type =~ /^(user|theme|i18n)$/) {
my $par = $lay->{'b2lid'} + 0;
LJ::S2::load_layer_info($layinf, [$par]);
$name = $layinf->{$par}->{'name'};
}
# Only use the layer name if there is one and it's more than just whitespace
my $title = "[$type] ";
$title .= $name && $name =~ /[^\s]/ ? "$name [\#$id]" : "Layer \#$id";
return "<title>" . LJ::ehtml($title) . " - Edit</title>\n" . $ret;
}
_code?>

View File

@@ -0,0 +1,260 @@
<?_code # -*-bml-*-
{
use strict;
use vars qw(%GET %POST $title $body);
LJ::set_active_crumb('yourlayers');
my $remote;
# authas switcher form
my $authasform = sub {
$body .= "<form method='get' action='styles.bml'>\n";
$body .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'} }) . "\n";
$body .= "</form>\n\n";
};
# used for error messages
my $err = sub {
$title = "Error";
$body = '';
$authasform->() if $remote;
$body .= "<?p $_[0] p?>";
return;
};
# id is optional
my $id = $POST{'id'} if $POST{'id'} =~ /^\d+$/;
# this catches core_hidden if it's set
$POST{'parid'} ||= $POST{'parid_hidden'};
# authenticate user
$remote = LJ::get_remote();
return $err->('You must be logged in to view your layers.')
unless $remote;
my $authas = $GET{'authas'} || $remote->{'user'};
my $u = LJ::get_authas_user($authas);
# if we don't have a u, maybe they're an admin and can view stuff anyway?
my $noactions = 0;
if ($GET{user} && (LJ::check_priv($remote, 'canview', 'styles') ||
LJ::check_priv($remote, 'canview', '*'))) {
return $err->('This privilege cannot be used on the system account.')
if $GET{user} eq 'system';
$u = LJ::load_user($GET{user});
$noactions = 1; # don't let admins change anything
}
return $err->('You could not be authenticated as the specified user.')
unless $u;
# load user and public layers
my $pub = LJ::S2::get_public_layers();
my $ulay = LJ::S2::get_layers_of_user($u);
my $has_priv = LJ::get_cap($u, 's2styles');
return $err->($remote->{user} eq $u->{user} ?
'Your account type does not allow advanced customization.' :
'The selected user\'s account type does not allow advanced customization.' )
unless $has_priv;
# start of output
$title = "Your Layers";
$body .= BML::ml("backlink", {
'link' => './',
'text' => 'Advanced Customization',
}) . "\n";
$body .= BML::ml("actionlink", {
'link' => "<a href='styles.bml?authas=$authas'>Your Styles</a>",
}) . "\n";
### perform actions ###
# create
if ($POST{'action:create'} && !$noactions) {
return $err->("You have reached your maximum number of allowed layers")
if keys %$ulay >= LJ::get_cap($u, 's2layersmax');
my $err_badparid = "No/bogus parent layer ID given (for layouts and core languages, use core parent ID; for themes and layout languages, use layout ID)";
my $type = $POST{'type'} or return $err->("No layer type selected.");
my $parid = $POST{'parid'}+0 or return $err->($err_badparid);
return $err->("Invalid layer type") unless $type =~ /^layout|theme|user|i18nc?$/;
my $parent_type = ($type eq "theme" || $type eq "i18n" || $type eq "user") ? "layout" : "core";
# parent ID is public layer
if ($pub->{$parid}) {
# of the wrong type
return $err->($err_badparid) if $pub->{$parid}->{'type'} ne $parent_type;
# parent ID is user layer, or completely invalid
} else {
return $err->($err_badparid) if
! $ulay->{$parid} || $ulay->{$parid}->{'type'} != $parent_type;
}
my $id = LJ::S2::create_layer($u, $parid, $type);
return $err->("Error creating layer") unless $id;
my $lay = {
'userid' => $u->{'userid'},
'type' => $type,
'b2lid' => $parid,
's2lid' => $id,
};
# help user out a bit, creating the beginning of their layer.
my $s2 = "layerinfo \"type\" = \"$type\";\n";
$s2 .= "layerinfo \"name\" = \"\";\n\n";
my $error;
unless (LJ::S2::layer_compile($lay, \$error, { 's2ref' => \$s2 })) {
return $err->("Error setting up &amp; compiling layer: $error");
}
# redirect so they can't refresh and create a new layer again
return BML::redirect("layers.bml?authas=$authas");
}
# delete
if ($POST{'action:del'} && !$noactions) {
my $id = $POST{'id'}+0;
my $lay = LJ::S2::load_layer($id);
return $err->("The specified layer does not exist")
unless $lay;
return $err->("You do not own the specified layer")
unless $lay->{'userid'} == $u->{'userid'};
unless ($POST{'confirm'}) {
my $layerinfo = {};
LJ::S2::load_layer_info($layerinfo, [ $id ]);
my $name = $layerinfo->{$id}->{'name'} ? "'$layerinfo->{$id}->{'name'}'" : "#$id";
$name = LJ::ehtml($name);
$title = "Deleting layer $name";
$body .= "<br /> ";
$body .= BML::ml("backlink", {
'link' => "layers.bml?authas=$authas",
'text' => 'Your Layers',
}) . "\n";
$body .= "<form method='post' action='layers.bml?authas=$authas'>";
$body .= LJ::html_hidden('action:del', '1', 'id', $id);
$body .= "Are you sure you want to delete $lay->{'type'} layer $name?";
$body .= "<p>" . LJ::html_submit('confirm', 'Delete') . "</p>\n";;
$body .= "</form>\n";
return;
}
LJ::S2::delete_layer($u, $id);
return BML::redirect("layers.bml?authas=$authas");
}
# authas switcher form
unless ($noactions) {
$authasform->();
}
# show list of layers
$body .= "<?h1 Your Layers h1?>\n";
if (%$ulay) {
$body .= "<table style='margin-bottom: 10px' cellpadding='3' border='1'>\n";
$body .= "<tr><td><b>LayerID</b></td><td><b>Type</b></td><td><b>Name</b></td><td><b>Actions</b></td></tr>\n";
my $lastbase = 0;
foreach my $lid (sort { $ulay->{$a}->{'b2lid'} <=> $ulay->{$b}->{'b2lid'} || $a <=> $b }
keys %$ulay)
{
my $bid = $ulay->{$lid}->{'b2lid'};
if ($bid != $lastbase) {
$lastbase = $bid;
my $parlay = $ulay->{$bid} || $pub->{$bid};
my $pname = LJ::ehtml($parlay->{'name'});
$body .= "<tr><td colspan='4'><small>Child of <a href='layerbrowse.bml?id=$bid'>layer $bid</a>: $pname</small></td></tr>\n";
}
my $lay = $ulay->{$lid};
my $name = LJ::ehtml($lay->{'name'}) || "<i>(none)</i>";
$body .= "<tr><td><a href='layerbrowse.bml?id=$lid'>$lid</a></td><td>$lay->{'type'}</td><td>$name</td><td>";
$body .= "<form method='post' style='display:inline' action='layeredit.bml?id=$lid'>";
$body .= LJ::html_submit('action:edit', 'Edit...', { disabled => $noactions });
$body .= "</form>";
$body .= "<form method='post' style='display:inline' action='layers.bml?authas=$authas'>";
$body .= LJ::html_hidden('id', $lid);
$body .= LJ::html_submit('action:del', 'Delete...', { disabled => $noactions });
$body .= "</form>";
$body .= "</td></tr>\n"
}
$body .= "</table>\n\n";
} else {
$body .= "<?p <i>None</i> p?>\n\n";
}
# jump out if we're just viewing
return if $noactions;
# create layer
$body .= "<?h1 Create Layer h1?>\n";
$body .= "<div style='margin-top: 10px;'>\n";
$body .= "<?h2 Create top-level layer h2?>\n";
$body .= "<form method='post' action='layers.bml?authas=$authas'>\n";
$body .= "Type: " . LJ::html_select({ 'name' => 'type' },
"" => "",
"layout" => "Layout",
"i18nc" => "Language",
) . "\n";
my @corelayers = map { $_, $pub->{$_}->{'majorversion'} }
sort { $pub->{$b}->{'majorversion'} <=> $pub->{$a}->{'majorversion'} }
grep { $pub->{$_}->{'b2lid'} == 0 && $pub->{$_}->{'type'} eq 'core' && /^\d+$/}
keys %$pub;
$body .= " Core Version: " . LJ::html_select({ 'name' => 'parid',
'selected' => $corelayers[0],
'disabled' => @corelayers > 2 ? 0: 1 },
@corelayers ) . "\n";
# store value in hidden to later be copied to 'parid' if necessary
# defaults to $corelayers[0] which should be the highest numbered core
$body .= LJ::html_hidden("parid_hidden", $POST{'parid'} || $corelayers[0]) . "\n";
$body .= LJ::html_submit("action:create", "Create") . "\n";
$body .= "</form>\n";
$body .= "</div>\n\n";
$body .= "<?h2 Create layout-specific layer h2?>\n";
$body .= "<form method='post' action='layers.bml?authas=$authas'>\n";
$body .= "Type: " . LJ::html_select({ 'name' => 'type' },
"" => "",
"theme" => "Theme",
"i18n" => "Language",
"user" => "User"
) . "\n";
my @layouts = ('', '');
push @layouts, map { $_, $pub->{$_}->{'name'} }
sort { $pub->{$a}->{'name'} cmp $pub->{$b}->{'name'} || $a <=> $b}
grep { $pub->{$_}->{'type'} eq 'layout' && /^\d+$/}
keys %$pub;
if (%$ulay) {
my @ulayouts = ();
push @ulayouts, map { $_, "$ulay->{$_}->{'name'} (#$_)" }
sort { $ulay->{$a}->{'name'} cmp $ulay->{$b}->{'name'} || $a <=> $b}
grep { $ulay->{$_}->{'type'} eq 'layout' }
keys %$ulay;
push @layouts, ('', '---', @ulayouts) if @ulayouts;
}
$body .= "Layout: " . LJ::html_select({ 'name' => 'parid' }, @layouts) . "\n";
$body .= LJ::html_submit("action:create", "Create") . "\n";
$body .= "</form>\n\n";
return;
}
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?>

View File

@@ -0,0 +1,78 @@
<?_code # -*-bml-*-
{
use strict;
use vars qw(%GET);
my $pub = LJ::S2::get_public_layers();
# for error reporting
my $err = sub {
return "<h2>Error</h2>" . shift;
};
my $dbr = LJ::get_db_reader();
my $remote = LJ::get_remote();
my $id = $GET{'id'};
return BML::redirect('layerbrowse.bml') unless $id =~ /^\d+$/;
my $lay = defined $pub->{$id} ? $pub->{$id} : LJ::S2::load_layer($id);
return $err->("The specified layer does not exist.")
unless $lay;
my $layerinfo = {};
LJ::S2::load_layer_info($layerinfo, [ $id ]);
my $srcview = exists $layerinfo->{$id}->{'source_viewable'} ?
$layerinfo->{$id}->{'source_viewable'} : undef;
# authorized to view this layer?
my $isadmin = !defined $pub->{$id} && # public styles are pulled from the system
(LJ::check_priv($remote, 'canview', 'styles') || # account, so we don't want to check privileges
LJ::check_priv($remote, 'canview', '*')); # in case they're private styles
return $err->("You are not authorized to view this layer.")
unless defined $pub->{$id} && (! defined $srcview || $srcview != 0) ||
$srcview == 1 ||
LJ::can_manage($remote, $lay->{'userid'}) ||
$isadmin;
my $s2code = $dbr->selectrow_array("SELECT s2code FROM s2source WHERE s2lid=?", undef, $id);
# get html version of the code?
if ($GET{'fmt'} eq "html") {
my $html;
my ($md5, $save_cache);
if ($pub->{$id}) {
# let's see if we have it cached
$md5 = Digest::MD5::md5_hex($s2code);
my $cache = $dbr->selectrow_array("SELECT value FROM blobcache WHERE bckey='s2html-$id'");
if ($cache =~ s/^\[$md5\]//) {
$html = $cache;
} else {
$save_cache = 1;
}
}
unless ($html) {
my $cr = new S2::Compiler;
$cr->compile_source({
'source' => \$s2code,
'output' => \$html,
'format' => "html",
'type' => $pub->{$id}->{'type'},
});
}
if ($save_cache) {
my $dbh = LJ::get_db_writer();
$dbh->do("REPLACE INTO blobcache (bckey, dateupdate, value) VALUES (?,NOW(),?)",
undef, "s2html-$id", "[$md5]$html");
}
return $html;
}
# return text version
BML::set_content_type("text/plain");
BML::noparse();
return $s2code;
}
_code?>

View File

@@ -0,0 +1,465 @@
<?_code # -*-bml-*-
{
use strict;
use vars qw(%GET %POST $title $body);
LJ::set_active_crumb('yourstyles');
my $remote;
# authas switcher form
my $authasform = sub {
$body .= "<form method='get' action='styles.bml'>\n";
$body .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'} }) . "\n";
$body .= "</form>\n\n";
};
# used for error messages
my $err = sub {
$title = "Error";
$body = '';
$authasform->() if $remote;
$body .= "<?p $_[0] p?>";
return;
};
# authenticate user
$remote = LJ::get_remote();
return $err->('You must be logged in to view your styles.')
unless $remote;
my $authas = $GET{'authas'} || $remote->{'user'};
my $u = LJ::get_authas_user($authas);
return $err->('You could not be authenticated as the specified user.')
unless $u;
return $err->($remote->{user} eq $u->{user} ?
'Your account type does not allow advanced customization.' :
'The selected user\'s account type does not allow advanced customization.' )
unless LJ::get_cap($u, 's2styles');
# extra arguments for get requests
my $getextra = $authas ne $remote->{'user'} ? "?authas=$authas" : '';
my $getextra_amp = "&authas=$authas" if $getextra;
# style id to edit, if we have one
# if we have this we're assumed to be in 'edit' mode
my $id = $GET{'id'}+0;
my $dbh = LJ::get_db_writer();
# variables declared here, but only filled in if $id
my ($core, $layout); # scalars
my ($pub, $ulay, $style); # hashrefs
# start of output
$title = "Styles";
$body = BML::ml("backlink", {
'link' => './',
'text' => 'Advanced Customization',
}) . "\n";
$body .= BML::ml("actionlink", {
'link' => "<a href='layers.bml$getextra'>Your Layers</a>",
}) . "\n";
# edit mode
if ($id) {
# load style
$style = LJ::S2::load_style($id);
return $err->('Style not found') unless $style;
# check that they own the style
return $err->("You do not own this style.")
unless $style->{'userid'} == $u->{'userid'};
# use selected style
if ($POST{'action:usestyle'}) {
# save to db and update user object
LJ::set_userprop($u, "stylesys", '2');
LJ::set_userprop($u, "s2_style", $id);
return BML::redirect("styles.bml$getextra");
}
# get public layers
$pub = LJ::S2::get_public_layers();
# get user layers
$ulay = LJ::S2::get_layers_of_user($u);
# find effective layerids being used
my %eff_layer = ();
my @other_layers = ();
foreach (qw(i18nc layout theme i18n user)) {
my $lid = $POST{$_} eq "_other" ? $POST{"other_$_"} : $POST{$_};
next unless $lid;
$eff_layer{$_} = $lid;
unless ($ulay->{$eff_layer{$_}} || $pub->{$eff_layer{$_}}) {
push @other_layers, $lid;
}
}
# core lid (can't use user core layer)
$POST{'core'} ||= $POST{'core_hidden'};
$core = defined $POST{'core'} ? $POST{'core'} : $style->{'layer'}->{'core'};
unless ($core) { # default to highest numbered core
map { $core = $_ if $pub->{$_}->{'type'} eq 'core' && /^\d+$/ &&
$pub->{$_}->{'majorversion'} > $pub->{$core}->{'majorversion'} } keys %$pub;
# update in POST to keep things in sync
$POST{'core'} = $core;
}
# layout lid
$layout = $POST{'action:change'} ? $eff_layer{'layout'} : $style->{'layer'}->{'layout'};
# if we're changing core, clear everything
if ($POST{'core'} && $style->{'layer'}->{'core'} &&
$POST{'core'} != $style->{'layer'}->{'core'}) {
foreach (qw(i18nc layout theme i18n user)) {
delete $eff_layer{$_};
}
undef $layout;
}
# if we're changing layout, clear everything below
if ($eff_layer{'layout'} && $style->{'layer'}->{'layout'} &&
$eff_layer{'layout'} != $style->{'layer'}->{'layout'}) {
foreach (qw(theme i18n user)) {
delete $eff_layer{$_};
}
}
# set up start of output
$title = "Edit Style";
$body .= "<br />" . BML::ml('backlink', { 'text' => 'Your Styles', 'link' => "styles.bml$getextra" }) . "\n";
### process edit actions
# delete
if ($POST{'action:delete'}) {
LJ::S2::delete_user_style($u, $id);
undef $id; # don't show form below
return BML::redirect("styles.bml$getextra");
}
# save changes
if ($POST{'action:change'} || $POST{'action:savechanges'}) {
# are they renaming their style?
if ($POST{'stylename'} && $style->{'name'} &&
$POST{'stylename'} ne $style->{'name'}) {
# update db
my $styleid = $style->{'styleid'};
$dbh->do("UPDATE s2styles SET name=? WHERE styleid=? AND userid=?",
undef, $POST{'stylename'}, $styleid, $u->{'userid'});
LJ::MemCache::delete([$styleid, "s2s:$styleid"]);
# update style object
$style->{'name'} = $POST{'stylename'};
}
# load layer info of any "other" layers
my %other_info = ();
if (@other_layers) {
LJ::S2::load_layer_info(\%other_info, \@other_layers);
foreach (@other_layers) {
return $err->("Layer not found: $_") unless exists $other_info{$_};
return $err->("Layer not public: $_") unless $other_info{$_}->{'is_public'};
}
}
# error check layer modifications
my $get_layername = sub {
my $lid = shift;
my $name;
$name = $pub->{$lid}->{'name'} if $pub->{$lid};
$name ||= $ulay->{$lid}->{'name'} if $ulay->{$lid};
$name ||= "#$lid";
return $name;
};
# check layer hierarchy
my $error_check = sub {
my ($type, $parentid) = @_;
my $lid = $eff_layer{$type};
next if ! $lid;
my $layer = $ulay->{$lid} || $pub->{$lid} || LJ::S2::load_layer($lid);
my $parentname = $get_layername->($parentid);
my $layername = $get_layername->($lid);
# is valid layer type?
return "Invalid layer type: <i>$layername</i> is not a $type layer"
if $layer->{'type'} ne $type;
# is a child?
return "Layer hierarchy mismatch: <i>$layername</i> is not a child $type layer of <i>$parentname</i>"
unless $layer->{'b2lid'} == $parentid;
return undef;
};
# check child layers of core
foreach my $type (qw(i18nc layout)) {
my $errmsg = $error_check->($type, $core);
return $err->($errmsg) if $errmsg;
}
# don't check sub-layout layers if there's no layout
if ($layout) {
# check child layers of selected layout
foreach my $type (qw(theme i18n user)) {
my $errmsg = $error_check->($type, $layout);
return $err->($errmsg) if $errmsg;
}
}
# save in database
my @layers = ( 'core' => $core );
push @layers, map { $_, $eff_layer{$_} } qw(i18nc layout i18n theme user);
LJ::S2::set_style_layers($u, $style->{'styleid'}, @layers);
# redirect if they clicked the bottom button
return BML::redirect("styles.bml$getextra") if $POST{'action:savechanges'};
}
# no style id, process actions for non-edit mode
# and load in data necessary for style list
} else {
# load user styles
my $ustyle = LJ::S2::load_user_styles($u);
# process create action
if ($POST{'action:create'} && $POST{'stylename'}) {
return $err->('You have reached your maximum number of styles.')
if scalar(keys %$ustyle) >= LJ::get_cap($u, 's2stylesmax');
my $styleid = LJ::S2::create_style($u, $POST{'stylename'});
return $err->('Style not created: Database error') unless $styleid;
return BML::redirect("styles.bml?id=$styleid$getextra_amp");
}
# load style currently in use
LJ::load_user_props($u, 's2_style');
# set up page header
$title = "Your Styles";
$authasform->();
$body .= "<div><?h1 Your Styles h1?></div>\n";
# show style listing
$body .= "<table style='margin-left: 40px'>\n";
if (%$ustyle) {
my $journalbase = LJ::journal_base($u);
foreach my $styleid (sort { $ustyle->{$a} cmp $ustyle->{$b} || $a <=> $b} keys %$ustyle) {
$body .= "<tr><td><form style='display:inline' method='post' action='styles.bml?id=$styleid$getextra_amp'>";
my @b = $styleid == $u->{'s2_style'} ? "<b>" : "</b>";
$body .= $b[0] . LJ::ehtml($ustyle->{$styleid});
$body .= " (<a href='$journalbase/?s2id=$styleid'>\#$styleid</a>)$b[1] ";
$body .= "</td><td>";
$body .= LJ::html_submit('action:edit', 'Edit') . " ";
$body .= LJ::html_submit('action:delete', 'Delete',
{ 'onclick' => "return confirm('Are you sure you want to delete style \#$styleid?')" }) . " ";
$body .= LJ::html_submit('action:usestyle', 'Use', { 'disabled' => $styleid == $u->{'s2_style'} }),
$body .= "</form></td></tr>\n";
}
} else {
$body .= "<tr><td><i>none</i></td></tr>\n";
}
$body .= "</table>\n";
}
### show create / edit form
my $extra = $id ? "?id=$id" : '';
$extra .= $extra ? $getextra_amp : $getextra;
$body .= "<form name='styleForm' method='post' action='styles.bml$extra'>";
# create a new style, or change the name of the style currently being edited
# note: this little bit of code appears whether there is an id passed or not.
# the textbox just has a different purpose depending on the context.
$body .= "<?h1 " . ($id ? "Style Options" : "Create Style") . " h1?>\n";
$body .= "<table style='margin-bottom: 10px'>\n";
$body .= "<tr><td>Name: </td><td>";
$body .= LJ::html_text({ 'name' => 'stylename', 'size' => '30', 'maxlength' => '255',
'value' => defined $POST{'stylename'} ? $POST{'stylename'} : $style->{'name'} });
$body .= " " . LJ::html_submit('action:create', 'Create') unless $id;
$body .= "</td></tr>\n";
$body .= "</table>\n";
# if no id to edit, we're finished
$body .= "</form>\n", return unless $id;
# from here on we have $pub, $ulay, and $style filled in
# sub to take a layer type, core, and parent layout
# and return a list of options to feed to LJ::html_select()
my $layerselect = sub {
my ($type, $b2lid) = @_;
my @opts = ();
# returns html_select to caller
my $html_select = sub {
my $dis = scalar(@opts) > 2 ? 0 : 1;
my $lid = $POST{'action:change'} ? $POST{$type} : $style->{'layer'}->{$type};
$lid = $POST{"other_$type"} if $lid eq "_other";
my $sel = ($lid && ! $pub->{$lid} && ! $ulay->{$lid}) ? "_other" : $lid;
return [ LJ::html_select({ 'name' => $type, 'id' => "select_$type",
'onChange' => "showOther('$type')",
'selected' => $sel,
'disabled' => $dis }, @opts), { 'disabled' => $dis, } ];
};
# greps, and sorts a list
my $greplist = sub {
my $ref = shift;
return sort { $ref->{$a}->{'name'} cmp $ref->{$b}->{'name'} || $a <=> $b}
grep { $ref->{$_}->{'type'} eq $type && $ref->{$_}->{'b2lid'} == $b2lid && /^\d+$/}
keys %$ref;
};
# public layers
my $name = $type eq 'core' ? 'majorversion' : 'name';
push @opts, map { $_, $pub->{$_}->{$name} } $greplist->($pub);
# no user core layers
return $html_select->() if $type eq 'core';
# user layers
push @opts, ('', '---');
my $startsize = scalar(@opts);
push @opts, map { $_, "$ulay->{$_}->{'name'} (\#$_)" } $greplist->($ulay);
# if we didn't push anything above, remove dividing line
pop @opts, pop @opts unless scalar(@opts) > $startsize;
# add option for other layerids
push @opts, ('_other', 'Other ...');
# add blank option to beginning of list
unshift @opts, ('', @opts ? '' : ' ');
return $html_select->();
};
my $layerother = sub {
my $name = shift;
my $olid = $POST{'action:change'} ? $POST{"other_$name"} : $style->{'layer'}->{$name};
my $disp = 'none';
my $val;
if ($olid && ! $pub->{$olid} && ! $ulay->{$olid}) {
$disp = 'inline';
$val = $olid;
}
return "<div id='layer_$name' style='margin-left: 5px; display: $disp;'>Layerid: " .
LJ::html_text({ 'name' => "other_$name", 'id' => "other_$name",
'size' => 6, 'value' => $val }) .
"</div>";
};
### core version
$body .= "<?h1 Style Layers h1?>\n";
$body .= "<table>\n";
$body .= "<tr><td>Core Version: </td><td>";
my $coresel = $layerselect->('core', 0);
$body .= $coresel->[0];
$body .= LJ::html_hidden('core_hidden', $core);
my $dis = $coresel->[1]->{'disabled'} ? { 'disabled' => 'disabled' } : undef;
$body .= " " . LJ::html_submit('action:change', 'Change', $dis) . "</td></tr>\n";
$body .= "</table>\n";
### i18nc / layout
$body .= "<table style='margin: 10px 0 0 40px'>\n";
# i18nc
$body .= "<tr><td>Language (i18nc): </td><td>";
$body .= $layerselect->('i18nc', $core)->[0];
$body .= $layerother->('i18nc');
$body .= "</td></tr>\n";
# layout
$body .= "<tr><td>Layout: </td><td>";
my $layoutsel = $layerselect->('layout', $core);
$body .= $layoutsel->[0];
$body .= $layerother->('layout');
my $dis = $layoutsel->[1]->{'disabled'} ? { 'disabled' => 'disabled' } : undef;
$body .= " " . LJ::html_submit("action:change", "Change", $dis) . " </td></tr>\n";
$body .= "</table>\n";
# do we need to show the rest of the form?
$body .= "</form>\n", return unless $layout;
### theme / i18n / user
# theme
$body .= "<table style='margin: 10px 0 0 80px'>\n";
$body .= "<tr><td>Language (i18n): </td><td>";
$body .= $layerselect->('i18n', $layout)->[0];
$body .= $layerother->('i18n') . "</td></tr>\n";
$body .= "<tr><td>Theme: </td><td>";
$body .= $layerselect->('theme', $layout)->[0];
$body .= $layerother->('theme') . "</td></tr>\n";
$body .= "<tr><td>User: </td><td>";
$body .= $layerselect->('user', $layout)->[0];
$body .= $layerother->('user') . "</td></tr>\n";
$body .= "<tr><td>&nbsp;</td><td>";
$body .= LJ::html_submit('action:savechanges', 'Save Changes') . "</td></tr>\n";
$body .= "</table>\n";
# end edit form
$body .= "</form>\n";
return;
}
_code?><?page
title=><?_code return $title; _code?>
head<=
<script language="JavaScript">
function showOther (name) {
if (! document.getElementById) return false;
var box = document.getElementById("other_" + name);
var list = document.getElementById("select_" + name);
var div = document.getElementById("layer_" + name);
if (div && box) {
div.style.display =
(list.value == "_other" && box.value != '' || list.value == "_other")
? "inline" : "none";
}
return false;
}
function pageload () {
if (!document.getElementById) return false;
var layers = new Array('i18nc', 'layout', 'i18n', 'theme', 'user');
for (var i=0; i<layers.length; i++) {
showOther(layers[i]);
}
return false;
}
</script>
<=head
body=><?_code return $body; _code?>
bodyopts=>onLoad="pageload();"
page?>

View File

@@ -0,0 +1,361 @@
<?page
title=><?_ml .title _ml?>
head<=
<style>
option.disabled { color: GrayText; }
</style>
<=head
body<=
<?_code
{
use strict;
use vars qw(%GET %POST %FORM);
LJ::set_active_crumb('customize');
my $remote = LJ::get_remote();
return LJ::bad_input($ML{'error.noremote'})
unless $remote;
my $authas = $GET{'journal'} || $GET{'authas'} || $remote->{'user'};
my $u = LJ::get_authas_user($authas);
return LJ::bad_input($ML{'error.invalidauth'})
unless $u;
my $userid = $u->{'userid'};
my $has_cap = LJ::get_cap($u, "s2styles");
return $LJ::MSG_READONLY_USER if LJ::get_cap($u, "readonly");
my $ret;
# authas switcher form
$ret .= "<form method='get' action='index.bml'>\n";
$ret .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'} || $GET{'journal'} }) . "\n";
$ret .= "</form>\n\n";
LJ::load_user_props($u, "stylesys", "s2_style");
$u->{'stylesys'} ||= 1;
my $pub = LJ::S2::get_public_layers();
my $userlay = LJ::S2::get_layers_of_user($u);
my %style = LJ::S2::get_style($u, "verify");
my $get_lang = sub {
my $styleid = shift;
foreach ($userlay, $pub) {
return $_->{$styleid}->{'langcode'} if
$_->{$styleid} && $_->{$styleid}->{'langcode'};
}
return undef;
};
my $langcode = $get_lang->($style{'i18n'}) || $get_lang->($style{'i18nc'});
if ($POST{'save:stylesys'}) {
my $num = $POST{'stylesys'} == 2 ? 2 : 1;
LJ::set_userprop($u, "stylesys", $num);
return BML::redirect("/customize/?journal=$u->{'user'}");
}
my $implicit_style_create = sub {
# create new style if necessary
unless ($u->{'s2_style'}) {
my $layid = $style{'layout'};
my $lay = $pub->{$layid} || $userlay->{$layid};
my $uniq = (split("/", $lay->{'uniq'}))[0] || $lay->{'s2lid'};
unless ($u->{'s2_style'} = LJ::S2::create_style($u, "wizard-$uniq")) {
die "<?h1 $ML{'Error'} h1?><?p $ML{'.error.fail_create_style'} p?>";
}
LJ::set_userprop($u, "s2_style", $u->{'s2_style'});
}
# save values in %style to db
LJ::S2::set_style_layers($u, $u->{'s2_style'}, %style);
};
if ($POST{'save:layout'}) {
my $layid = $POST{'layoutid'}+0;
return BML::redirect("/customize/?journal=$u->{'user'}")
if $layid == $style{'layout'};
my $lay = $pub->{$layid};
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.disallowed_user_layer'} p?>"
if ! $lay and $lay ||= $userlay->{$layid} and ! $has_cap;
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.not_your_layout'} p?>"
unless $lay && LJ::S2::can_use_layer($u, $lay->{'uniq'});
my $coreid = $lay->{'b2lid'};
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.no_core_parent'} p?>"
unless $coreid;
# delete s2_style and replace it with a new
# or existing style for this layout
delete $u->{'s2_style'};
my $uniq = (split("/", $lay->{'uniq'}))[0] || $lay->{'s2lid'};
my $usersty = LJ::S2::load_user_styles($u);
foreach (keys %$usersty) {
next unless $usersty->{$_} eq "wizard-$uniq";
$u->{'s2_style'} = $_;
LJ::set_userprop($u, "s2_style", $u->{'s2_style'});
# now we have to populate %style from this style, but not core and layout,
# as those are reset below
my $stylay = LJ::S2::get_style_layers($u, $u->{'s2_style'});
foreach my $layer (qw(user theme i18nc i18n)) {
$style{$layer} = exists $stylay->{$layer} ? $stylay->{$layer} : 0;
}
last;
}
# no existing style found, create a new one
unless ($u->{'s2_style'}) {
$style{'user'} = $style{'theme'} = $style{'i18nc'} = $style{'i18n'} = 0;
}
# even if we're using an existing style that we found by name (uniq), we need to
# set layout and core layers to make sure the style still has the proper layout
$style{'layout'} = $layid;
$style{'core'} = $coreid;
$implicit_style_create->();
return BML::redirect("/customize/?journal=$u->{'user'}");
}
if ($POST{'action:deluser'}) {
LJ::S2::delete_layer($style{'user'});
LJ::S2::set_style_layers($u, $u->{'s2_style'}, "user", 0) if $style{'user'};
return BML::redirect("/customize/?journal=$u->{'user'}");
}
if ($POST{'action:edituser'}) {
unless ($style{'user'}) {
$style{'user'} = LJ::S2::create_layer($u, $style{'layout'}, "user");
return "<?h1 $ML{'Error'} h1?> <?p $ML{'.error.cant_generate_user_layer'} p?>"
unless $style{'user'};
}
$implicit_style_create->();
return BML::redirect("/customize/layer.bml?w=user&journal=$authas");
}
if ($POST{'save:theme'}) {
my $themeid = $POST{'themeid'}+0;
return BML::redirect("/customize/?journal=$u->{'user'}")
if $themeid == $style{'theme'};
my $lay = $pub->{$themeid};
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.disallowed_theme_layer'} p?>"
if ! $lay and $lay ||= $userlay->{$themeid} and ! $has_cap;
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.not_your_theme'} p?>" if $themeid && ! $lay;
$style{'theme'} = $themeid;
$implicit_style_create->();
# TODO: conflict resolution. check if there
# exists a user layer, and ask user if they want to
# override it using the theme exclusively.
return BML::redirect("/customize/?journal=$u->{'user'}");
}
if ($POST{'save:langcode'}) {
my $langcode = $POST{'langcode'};
return BML::redirect("/customize/?journal=$u->{'user'}")
if $langcode eq 'custom';
my @langs = LJ::S2::get_layout_langs($pub, $style{'layout'});
my ($i18n, $i18nc);
# scan for an i18n user layer
foreach (values %$userlay) {
last if
$_->{'b2lid'} == $style{'layout'} &&
$_->{'type'} eq 'i18n' &&
$_->{'langcode'} eq $langcode &&
($i18n = $_->{'s2lid'});
}
# scan for i18nc public layer and i18n layer if necessary
foreach (values %$pub) {
last if $i18nc && $i18n;
next if
! $i18nc &&
$_->{'type'} eq 'i18nc' &&
$_->{'langcode'} eq $langcode &&
($i18nc = $_->{'s2lid'});
next if
! $i18n &&
$_->{'b2lid'} == $style{'layout'} &&
$_->{'type'} eq 'i18n' &&
$_->{'langcode'} eq $langcode &&
($i18n = $_->{'s2lid'});
}
$style{'i18nc'} = $i18nc;
$style{'i18n'} = $i18n;
$implicit_style_create->();
# TODO: conflict resolution. check if there
# exists a user layer, and ask user if they want to
# override it using the theme exclusively.
return BML::redirect("/customize/?journal=$u->{'user'}");
}
# choose style system
$ret .= "<form method='post' action='./?journal=$u->{'user'}'>";
$ret .= "<?h1 $ML{'.choose.header'} h1?><?p $ML{'.choose'} p?>";
$ret .= "<blockquote>";
$ret .= LJ::html_select({ 'name' => 'stylesys', 'selected' => $u->{'stylesys'} },
1, "$ML{'.choose.s1'}",
2, "$ML{'.choose.s2'}");
$ret .= " <input type='submit' name='save:stylesys' value='$ML{'.change'}'>";
$ret .= "</blockquote>";
# no more options if they're using S1.
if ($u->{'stylesys'} == 1) {
$ret .= "<?h1 $ML{'.s1.header'} h1?><?p ";
$ret .= BML::ml('.s1.2', { aopts => "href='$LJ::SITEROOT/modify.bml?authas=$authas'" });
$ret .= " p?></form>";
return $ret;
}
# choose layout
my @layouts = map { $_, $pub->{$_}->{'name'} }
sort { $pub->{$a}->{'name'} cmp $pub->{$b}->{'name'} }
grep { my $tmp = $_;
$tmp =~ /^\d+$/ &&
$pub->{$tmp}->{'type'} eq "layout" &&
LJ::S2::can_use_layer($u, $pub->{$tmp}->{'uniq'}) &&
($_ = $tmp)
} keys %$pub;
# custom layers can will be shown in the "Custom Layers" and "Disabled Layers" groups
# depending on the user's account status. if they don't have the s2styles cap, then
# they will have all layers disabled, except for the one they are currently using.
my $custom_layer_list = sub {
my ($type, $ptype) = @_;
my @layers = ();
my @user = map { $_, $userlay->{$_}->{'name'} ? $userlay->{$_}->{'name'} : "\#$_" }
sort { $userlay->{$a}->{'name'} cmp $userlay->{$b}->{'name'} || $a <=> $b }
grep { /^\d+$/ && $userlay->{$_}->{'b2lid'} == $style{$ptype} &&
$userlay->{$_}->{'type'} eq $type &&
($has_cap || $_ == $style{$type}) }
keys %$userlay;
if (@user) {
push @layers, { value => "",
text => "--- Custom Layers: ---",
disabled => 1 }, @user;
}
unless ($has_cap) {
my @disabled =
map { { value => $_,
text => $userlay->{$_}->{'name'} ? $userlay->{$_}->{'name'} : "\#$_",
disabled => 1 } }
sort { $userlay->{$a}->{'name'} cmp $userlay->{$b}->{'name'} ||
$userlay->{$a}->{'s2lid'} <=> $userlay->{$b}->{'s2lid'} }
grep { /^\d+$/ && $userlay->{$_}->{'b2lid'} == $style{$ptype} &&
$userlay->{$_}->{'type'} eq $type && $_ != $style{$type} }
keys %$userlay;
if (@disabled) {
push @layers, { value => "",
text => "--- Disabled Layers: ---",
disabled => 1 }, @disabled;
}
}
return @layers;
};
# add user/disabled ones
push @layouts, $custom_layer_list->('layout', 'core');
$ret .= "<?h1 $ML{'.s2.layout.header'} h1?><?p $ML{'.s2.layout'} p?>";
$ret .= "<blockquote>";
$ret .= LJ::html_select({ 'name' => 'layoutid',
'selected' => $style{'layout'}, },
@layouts);
$ret .= " <input type='submit' name='save:layout' value='$ML{'.change'}'> <a href=\"preview.bml?journal=$u->{'user'}\">$ML{'.previews'}</a>";
$ret .= "</blockquote>";
# pick other stuff
$ret .= "<?h1 $ML{'.s2.customize.header'} h1?>";
$ret .= "<?p $ML{'.s2.customize'} p?>";
# langauge
my @langs = LJ::S2::get_layout_langs($pub, $style{'layout'});
# they have set a custom i18n layer
if ($style{'i18n'} &&
($style{'i18nc'} != $style{'i18n'} || ! defined $pub->{$style{'i18n'}})) {
push @langs, 'custom', $ML{'.s2.customize.language.custom'};
$langcode = 'custom';
}
$ret .= "<?h2 $ML{'.s2.customize.language.header'} h2?><?p $ML{'.s2.customize.language'} p?>";
$ret .= "<blockquote>";
$ret .= LJ::html_select({ 'name' => 'langcode',
'selected' => $langcode, },
0 => $ML{'.s2.customize.language.default'},
@langs);
$ret .= " <input type='submit' name='save:langcode' value='$ML{'.change'}'>";
$ret .= "</blockquote>";
# theme
my @themes = LJ::S2::get_layout_themes_select([$pub], $style{'layout'});
# add user/disabled ones
push @themes, $custom_layer_list->('theme', 'layout');
$ret .= "<?h2 $ML{'.s2.customize.themes.header'} h2?><?p p?>";
$ret .= "<blockquote>";
$ret .= LJ::html_select({ 'name' => 'themeid',
'selected' => $style{'theme'}, },
'0' => $ML{'.s2.customize.themes.default'},
@themes);
$ret .= " <input type='submit' name='save:theme' value='$ML{'.change'}'> <a href=\"themes.bml?journal=$u->{'user'}\">$ML{'.previews'}</a>";
$ret .= "</blockquote>";
# customize
$ret .= "<?h2 $ML{'.s2.customize.settings.header'} h2?><?p $ML{'.s2.customize.settings'} p?>";
$ret .= "<blockquote>";
if ($style{'user'}) {
$ret .= LJ::html_submit('action:edituser', $ML{'.s2.customize.settings.edit'});
$ret .= LJ::html_submit('action:deluser', $ML{'.s2.customize.settings.delete'},
{ 'onclick' => "return confirm('" . LJ::ejs($ML{'.s2.customize.settings.delete.confirm'}) . "')" });
} else {
$ret .= "<input type='submit' name='action:edituser' value='$ML{'.s2.customize.settings.new'}'>\n";
}
$ret .= "</blockquote>";
$ret .= "</form>";
$ret .= "<?h1 $ML{'.s2.related.header'} h1?>";
$ret .= "<dl><dt>$ML{'.s2.advanced.header'}</dt><dd>";
$ret .= $has_cap ? $ML{'.s2.advanced.permitted'} : $ML{'.s2.advanced.denied'};
$ret .= "</dd><dt><a href='/manage/links.bml?authas=$authas'>$ML{'/manage/links.bml.title'}</a></dt><dd>$ML{'.s2.related.links.about'}</dd>";
$ret .= "<dt><a href='/modify.bml?authas=$authas'>$ML{'/modify.bml.title'}</a></dt><dd>$ML{'.s2.related.modify.about'}</dd>";
$ret .= "<dt><a href='/editinfo.bml?authas=$authas'>$ML{'/editinfo.bml.title'}</a></dt><dd>$ML{'.s2.related.editinfo.about'}</dd></dl>";
return $ret;
}
_code?>
<=body
head<=
<style type='text/css'>
dt { font-weight: bold }
</style>
<=head
page?>

View File

@@ -0,0 +1,438 @@
<?_code # -*-bml-*-
{
use strict;
use vars qw(%POST %GET $title $body $js);
my $dbh = LJ::get_db_writer();
my $remote = LJ::get_remote();
return "<?h1 Login Required h1?><?p Before you customize your journal, you must first <a href='/login.bml'>login</a>. p?>"
unless $remote;
my @journals = ($remote->{'user'});
push @journals, LJ::get_shared_journals($remote);
my $journal = $GET{'journal'} || $remote->{'user'};
unless (grep { $_ eq $journal } @journals) { return BML::redirect("/customize/"); }
my $u = $remote;
$u = LJ::load_user($journal) unless $journal eq $remote->{'user'};
my $userid = $u->{'userid'};
LJ::load_user_props($u, "stylesys", "s2_style");
$body = "";
$title = "Customize";
$js = "";
my $err = sub {
$title = "Error";
$body = shift;
return;
};
my ($style, $layer);
my $save_arg;
my $save_redir;
# when given 'w' argument, load user's current style, and edit the user layer.
# this is the mode redirected to from /customize/ (the simple customization UI)
if ($GET{'w'} eq "user" && $u->{'stylesys'} == 2)
{
$style = LJ::S2::load_style($u->{'s2_style'});
return $err->("Style not found.") unless $style && $style->{'userid'} == $u->{'userid'};
$layer = LJ::S2::load_layer($dbh, $style->{'layer'}->{'user'});
# if the b2lid of this layer has been remapped to a new layerid
# then update the b2lid mapping for this layer
my $b2lid = $layer->{b2lid};
if ($b2lid && $LJ::S2LID_REMAP{$b2lid}) {
LJ::S2::b2lid_remap($u, $style->{'layer'}->{'user'}, $b2lid);
$layer->{b2lid} = $LJ::S2LID_REMAP{$b2lid};
}
$save_arg = "w=user&journal=$journal";
$save_redir = "/customize/?journal=$journal";
}
return BML::redirect("/customize/") unless $layer;
return $err->("Layer belongs to another user.") unless $layer->{'userid'} == $u->{'userid'};
return $err->("Layer isn't of type user or theme.")
unless $layer->{'type'} eq "user" || $layer->{'type'} eq "theme";
my $lyr_layout = LJ::S2::load_layer($dbh, $layer->{'b2lid'});
return $err->("Layout layer for this $layer->{'type'} layer not found.")
unless $lyr_layout;
my $lyr_core = LJ::S2::load_layer($dbh, $lyr_layout->{'b2lid'});
return $err->("Core layer for layout not found.")
unless $lyr_core;
$lyr_layout->{'uniq'} = $dbh->selectrow_array("SELECT value FROM s2info WHERE s2lid=? AND infokey=?",
undef, $lyr_layout->{'s2lid'}, "redist_uniq");
my ($lid_i18nc, $lid_theme, $lid_i18n);
$lid_i18nc = $style->{'layer'}->{'i18nc'};
$lid_theme = $style->{'layer'}->{'theme'};
$lid_i18n = $style->{'layer'}->{'i18n'};
my $layerid = $layer->{'s2lid'};
my @layers;
push @layers, ([ 'core' => $lyr_core->{'s2lid'} ],
[ 'i18nc' => $lid_i18nc ],
[ 'layout' => $lyr_layout->{'s2lid'} ],
[ 'i18n' => $lid_i18n ]);
if ($layer->{'type'} eq "user" && $lid_theme) {
push @layers, [ 'theme' => $lid_theme ];
}
push @layers, [ $layer->{'type'} => $layer->{'s2lid'} ];
my @layerids = grep { $_ } map { $_->[1] } @layers;
LJ::S2::load_layers(@layerids);
my %layerinfo;
# load the language and layout choices for core.
LJ::S2::load_layer_info(\%layerinfo, \@layerids);
# action path
if (LJ::did_post()) {
# prevent spoofing:
return BML::redirect("/customize")
unless $POST{'userid'} == $u->{'userid'};
my %override;
foreach my $prop (S2::get_properties($lyr_layout->{'s2lid'}))
{
$prop = S2::get_property($lyr_core->{'s2lid'}, $prop)
unless ref $prop;
next unless ref $prop;
next if $prop->{'noui'};
my $name = $prop->{'name'};
next unless $POST{"$name:override"};
next unless LJ::S2::can_use_prop($u, $lyr_layout->{'uniq'}, $name);
$override{$name} = [ $prop, $POST{"${name}_value"} ];
}
if (LJ::S2::layer_compile_user($layer, \%override)) {
return BML::redirect($save_redir) if $save_redir;
$body = "Saved.";
return;
} else {
my $error = LJ::last_error();
$body = "Error saving layer:<pre>$error</pre>";
}
return;
}
$body .= "<form method='post' action='layer.bml?$save_arg'>";
$body .= LJ::html_hidden("userid", $u->{'userid'});
my %prop; # name -> hashref, deleted when added to a category
my @propnames;
foreach my $prop (S2::get_properties($lyr_layout->{'s2lid'})) {
unless (ref $prop) {
$prop = S2::get_property($lyr_core->{'s2lid'}, $prop);
next unless ref $prop;
}
$prop{$prop->{'name'}} = $prop;
push @propnames, $prop->{'name'};
}
my @groups = S2::get_property_groups($lyr_layout->{'s2lid'});
my $misc_group;
my %groupprops; # gname -> [ propname ]
my %propgroup; # pname -> gname;
foreach my $gname (@groups) {
if ($gname eq "misc" || $gname eq "other") { $misc_group = $gname; }
foreach my $pname (S2::get_property_group_props($lyr_layout->{'s2lid'}, $gname)) {
my $prop = $prop{$pname};
next if ! $prop || $propgroup{$pname};
$propgroup{$pname} = $gname;
push @{$groupprops{$gname}}, $pname;
}
}
# put unsorted props into an existing or new unsorted/misc group
if (@groups) {
my @unsorted;
foreach my $pname (@propnames) {
my $prop = $prop{$pname};
next if ! $prop || $propgroup{$pname};
push @unsorted, $pname;
}
if (@unsorted) {
unless ($misc_group) {
$misc_group = "misc";
push @groups, "misc";
}
push @{$groupprops{$misc_group}}, @unsorted;
}
}
my $group_name = sub {
my $gname = shift;
foreach my $lid ($lid_i18n, $lyr_layout->{'s2lid'}, $lid_i18nc, $lyr_core->{'s2lid'}) {
next unless $lid;
my $name = S2::get_property_group_name($lid, $gname);
return LJ::ehtml($name) if $name;
}
return "Misc" if $gname eq "misc";
return $gname;
};
my $prop_js = "";
my $output_prop = sub {
my $name = shift;
my $prop = $prop{$name};
return if ! $prop || $prop->{'noui'};
my $name = $prop->{'name'};
my $type = $prop->{'type'};
my $can_use = LJ::S2::can_use_prop($u, $lyr_layout->{'uniq'}, $name);
# figure out existing value (if there was no user/theme layer)
my $existing;
foreach my $lid (reverse @layerids) {
next if $lid == $layerid;
$existing = S2::get_set($lid, $name);
last if defined $existing;
}
if (ref $existing eq "HASH") { $existing = $existing->{'as_string'}; }
if ($type eq "bool") {
$prop->{'values'} ||= "1|Yes|0|No";
}
my %values = split(/\|/, $prop->{'values'});
my $existing_display = defined $values{$existing} ?
$values{$existing} : $existing;
$existing_display = LJ::eall($existing_display);
my $override = S2::get_set($layerid, $name);
my $had_override = defined $override;
$override = $existing unless defined $override;
if (ref $override eq "HASH") { $override = $override->{'as_string'}; }
$body .= "<?h1 " . LJ::eall($prop->{'des'}) . " h1?>";
{
my $t = "";
$t .= LJ::eall($prop->{'note'}) if $prop->{'note'};
$t .= "<?help " . $LJ::HELPPURL{"s2opt_$name"} . " help?>" if $LJ::HELPURL{"s2opt_$name"};
$body .= "<?p $t p?>" if $t;
}
$body .= "<div class='inset'>\n";
$body .= LJ::html_check({ 'name' => "$name:override", 'id' => "$name:override",
'selected' => $had_override, 'disabled' => ! $can_use,
'onchange' => "toggleOverride('$name');" });
my $offhelp = ! $can_use ? LJ::help_icon('s2propoff', ' ') : "";
$body .= "<label for='$name:override'> Override default</label>$offhelp<br />\n";
$body .= "<dl class='hidedt'>\n<dt>Default:</dt>\n<dd id='$name:default_value' style='font-family: monospace'>\n";
if ($type eq "int" || $type eq "string") {
if ($existing_display ne "") {
$body .= $existing_display;
} else {
$body .= "<i>(nothing)</i>";
}
} elsif ($type eq "Color") {
$body .= "<span style=\"border: 1px solid #000000; padding-left: 2em; ".
"background-color: $existing;\">&nbsp;</span> <tt>$existing_display</tt>";
} elsif ($type eq "bool") {
$body .= $existing_display;
}
$body .= "</dd>\n<dt>Override:</dt>\n<dd id='$name:user_value'>\n";
if ($prop->{'values'}) {
$body .= LJ::html_select({ 'name' => "${name}_value",
'disabled' => ! $can_use,
'selected' => $override },
split(/\|/, $prop->{'values'}));
} elsif ($type eq "int") {
$body .= LJ::html_text({ 'name' => "${name}_value",
'disabled' => ! $can_use,
'value' => $override,
'maxlength' => 5,
'size' => 7 });
} elsif ($type eq "string") {
my ($rows, $cols) = ($prop->{'rows'} + 0,
$prop->{'cols'} + 0);
if ($rows > 0 && $cols > 0) {
$body .= LJ::html_textarea({ 'name' => "${name}_value",
'disabled' => ! $can_use,
'value' => $override,
'onfocus' => "toggleOverride('$name');",
'rows' => $rows,
'cols' => $cols });
} else {
my ($size, $maxlength) = ($prop->{'size'} || 30,
$prop->{'maxlength'} || 255);
$body .= LJ::html_text({ 'name' => "${name}_value",
'disabled' => ! $can_use,
'value' => $override,
'maxlength' => $maxlength,
'size' => $size });
}
} elsif ($type eq "Color") {
$body .= LJ::html_color({ 'name' => "${name}_value",
'disabled' => ! $can_use,
'default' => $override,
'des' => $prop->{'des'} });
}
$body .= "</dd></dl>\n</div>\n";
$prop_js .= "toggleOverride('$name');\n";
};
if (@groups) {
$body .= "<div id='propgroupstab'>";
my $num = 0;
foreach my $gname (@groups) {
my $name = $group_name->($gname);
$num++;
my $class = $num == 1 ? "propgrouptabsel" : "propgrouptab";
$js .= " propgroups[$num] = \"$gname\";\n";
$body .= "<span class='$class' id='pgroup_tab_$gname'><a href='#pgroup$gname' onclick=\"return showPropGroup('$gname')\">$name</a></span>\n";
}
$body .= "</div>";
$body .= "<div id='propgroupsbody'>";
foreach my $gname (@groups) {
$body .= "<a name='pgroup$gname'></a>\n";
$body .= "<div id='pgroup_body_$gname'>\n";
foreach my $pname (@{$groupprops{$gname}}) {
$output_prop->($pname);
}
$body .= "</div>";
}
$body .= "</div>\n";
$body .= "<script language='JavaScript'>showPropGroup('$groups[0]');</script>\n";
} else {
foreach my $pname (@propnames) {
$output_prop->($pname);
}
}
$body .= "<script type='text/javascript' language='JavaScript'><!--\n$prop_js// --></script>";
$body .= "<?h1 Finished? h1?><?p When done, click the save button below. p?><center>";
$body .= LJ::html_submit('action:save', "Save");
$body .= "</center>";
$body .= "</form>";
return;
}
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
head<=
<script language="JavaScript" src="<?_code return $LJ::JSPREFIX; _code?>/colorpicker.js"></script>
<script language="JavaScript">
colPic_set_imgprefix("<?_code return $LJ::IMGPREFIX; _code?>");
var propgroups = new Array();
function showPropGroup (name) {
if (! document.getElementById) { return true; }
for (var i=0; i<propgroups.length; i++) {
var gname = propgroups[i];
var tab = document.getElementById("pgroup_tab_" + gname);
if (tab && tab.setAttribute) {
tab.setAttribute("class", (gname == name) ? "propgrouptabsel" : "propgrouptab");
}
var div = document.getElementById("pgroup_body_" + gname);
if (div) {
div.style.display = (gname == name) ? "block" : "none";
}
}
return false;
}
function toggleOverride (name) {
if (!name) return true;
if (!document.getElementById) return true;
var ocheck = document.getElementById(name + ':override');
if (!ocheck) return true;
var def_vals = document.getElementById(name + ':default_value');
if (!def_vals) return true;
var usr_vals = document.getElementById(name + ':user_value');
if (!usr_vals) return true;
var disp_def = ocheck.checked ? 'none' : 'block';
var disp_usr = ocheck.checked ? 'block' : 'none';
def_vals.style.display = disp_def;
usr_vals.style.display = disp_usr;
}
<?_code return $js; _code?>
</script>
<style type='text/css'>
#propgroupstab {
font-size: 1.2em;
font-weight: bold;
font-decoration: none;
border-bottom: 2px solid black;
margin: 0; padding: 0;
}
#propgroupsbody {
border-left: 2px solid black;
border-right: 2px solid black;
border-bottom: 2px solid black;
margin: 0; padding: 0.5em;
}
span.propgrouptabsel, span.propgrouptab {
margin: 0 0.2em 0 0.8em;
padding: 0.25em;
border-top: 2px solid black;
border-left: 2px solid black;
border-right: 2px solid black;
}
span.propgrouptab {
background: #ddd;
color: black;
}
span.propgrouptab a {
text-decoration: none;
color: black;
}
span.propgrouptabsel {
color: #ddd;
background: black;
}
span.propgrouptabsel a {
text-decoration: none;
color: #ddd;
}
div.inset {
margin-left: 2em;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
dl.hidedt {
margin: 0;
padding: 0;
}
dl.hidedt dt { font-weight: bold; }
noscript { display: inline; }
</style>
<script type='text/javascript' language='JavaScript'>
// Only hide if we do the JS-switchy thing
if (document.getElementById)
document.write("<style type='text/css'>" +
"dl.hidedt dd { margin: 0; padding: 0; }" +
"dl.hidedt dt { display: none; }" +
"noscript { display: none; }" +
"</style>");
</script>
<=head
page?>

View File

@@ -0,0 +1,39 @@
<?page
title=>Layout Previews
body<=
<?_code
{
use strict;
use vars qw(%GET %POST);
LJ::set_active_crumb('preview');
my $ret;
my $remote = LJ::get_remote();
my $journal = LJ::canonical_username($GET{'journal'});
$ret .= "<p>[&lt;&lt; <a href='/customize/?journal=$journal'>Back</a>]</p>";
my $pub = LJ::S2::get_public_layers();
my @layouts =
sort { $a->{'name'} cmp $b->{'name'} }
map { $pub->{$_} } grep { /^\d+$/ && $pub->{$_}->{'type'} eq "layout" } keys %$pub;
foreach my $l (@layouts) {
$ret .= "<?h1 " . LJ::eall($l->{'name'}) . " h1?>";
unless ($l->{'_previews'}) {
$ret .= "<?p <i>No preview available.</i> p?>";
next;
}
foreach (split(/\,/, $l->{'_previews'})) {
my ($img, $w, $h) = split(/\|/, $_);
$ret .= "<p style='margin-left: 20px'><img src=\"$LJ::IMGPREFIX/s2preview/$img\" width=\"$w\" height=\"$h\"></p>";
}
}
return $ret;
}
_code?>
<=body
page?>

View File

@@ -0,0 +1,79 @@
<?page
title=>Theme Previews
body<=
<?_code
{
use strict;
use vars qw(%GET %POST);
LJ::set_active_crumb('themes');
my $ret;
my $dbr = LJ::get_db_reader();
my $remote = LJ::get_remote();
return "<?h1 Login Required h1?><?p Before you customize your journal, you must first <a href='/login.bml?ret=1'>login</a>. p?>"
unless $remote;
my @journals = ($remote->{'user'});
push @journals, LJ::get_shared_journals($remote);
my $journal = $GET{'journal'} || $remote->{'user'};
unless (grep { $_ eq $journal } @journals) { return BML::redirect("/customize/"); }
$ret .= "<p>[&lt;&lt; <a href='/customize/?journal=$journal'>Back</a>]</p>";
my $u = $remote;
$u = LJ::load_user($journal) unless $journal eq $remote->{'user'};
my $userid = $u->{'userid'};
LJ::load_user_props($u, "stylesys", "s2_style");
$u->{'stylesys'} ||= 1;
my $pub = LJ::S2::get_public_layers();
my $userlay = {}; # TODO: add API call to fetch these
my %style = LJ::S2::get_style($u->{'s2_style'});
return "S2 required" unless $u->{'stylesys'} == 2 && $style{'layout'};
# get themes for this layer
my @themes = LJ::S2::get_layout_themes([$pub, $userlay], $style{'layout'});
# make the context, without the theme
my @layers;
foreach (qw(core i18nc layout i18n)) {
push @layers, $style{$_} if $style{$_};
}
LJ::S2::load_layers(@layers, map { $_->{'s2lid'} } @themes);
my $cleaner = new HTMLCleaner ('output' => sub { $ret .= $_[0]; });
my $out_straight = sub { $ret .= $_[0]; };
my $out_clean = sub { $cleaner->parse($_[0]); };
$LJ::S2::CURR_PAGE = undef;
foreach my $t (0, @themes) {
my $id = ref $t ? $t->{'s2lid'} : 0;
my $name = ref $t ? LJ::eall($t->{'name'}) : "Layout Default";
my $ctx = S2::make_context(@layers, $id);
LJ::S2::escape_context_props($ctx);
LJ::S2::populate_system_props($ctx);
S2::set_output(sub {}); # printing suppressed
S2::set_output_safe(sub {});
eval { S2::run_code($ctx, "prop_init()"); };
S2::set_output($out_straight);
S2::set_output_safe($out_straight);
$ret .= "<?h1 $name h1?>";
$ret .= "<div>";
eval { S2::run_code($ctx, "print_theme_preview()"); };
$ret .= "</div>";
}
$cleaner->eof if $cleaner; # flush any remaining text/tag not yet spit out
return $ret;
}
_code?>
<=body
page?>

View File

@@ -0,0 +1,159 @@
<?_code # -*-bml-*-
{
use strict;
use vars qw(%POST %GET $title $body);
my $dbh = LJ::get_db_writer();
my $remote = LJ::get_remote();
return "<?h1 Login Required h1?><?p You must first <a href='/login.bml?ret=1'>login</a>. p?>"
unless $remote;
my @journals = ($remote->{'user'});
push @journals, LJ::get_shared_journals($remote);
my $journal = $GET{'journal'} || $remote->{'user'};
unless (grep { $_ eq $journal } @journals) { return BML::redirect("/customize/"); }
my $u = $remote;
$u = LJ::load_user($journal) unless $journal eq $remote->{'user'};
my $userid = $u->{'userid'};
LJ::load_user_props($u, "stylesys", "s2_style");
$body = "";
$title = "User Layer";
if (@journals > 1) {
$body .= "<form method='get' action='viewuser.bml'>";
$body .= "Work with journal: ";
$body .= LJ::html_select({
'name' => 'journal',
'selected' => $journal,
}, map { $_, $_ } @journals);
$body .= " <input type='submit' value='Switch'> </form>";
}
$body .= "&lt;&lt; <a href='/customize/?journal=$journal'>customize</a><p>";
my $err = sub {
$title = "Error";
$body = shift;
return;
};
my ($style, $layer);
my $save_arg;
my $save_redir;
# when given 'w' argument, load user's current style, and edit the user layer.
# this is the mode redirected to from /customize/ (the simple customization UI)
if ($u->{'stylesys'} == 2)
{
$style = LJ::S2::load_style($u->{'s2_style'});
return $err->("Style not found.") unless $style && $style->{'userid'} == $u->{'userid'};
$layer = LJ::S2::load_layer($dbh, $style->{'layer'}->{'user'});
}
unless ($layer) {
$body .= "No user layer";
return;
}
return $err->("Layer belongs to another user.") unless $layer->{'userid'} == $u->{'userid'};
return $err->("Layer isn't of type user or theme.")
unless $layer->{'type'} eq "user";
my $lyr_layout = LJ::S2::load_layer($dbh, $layer->{'b2lid'});
return $err->("Layout layer for this $layer->{'type'} layer not found.")
unless $lyr_layout;
my $lyr_core = LJ::S2::load_layer($dbh, $lyr_layout->{'b2lid'});
return $err->("Core layer for layout not found.")
unless $lyr_core;
$lyr_layout->{'uniq'} = $dbh->selectrow_array("SELECT value FROM s2info WHERE s2lid=? AND infokey=?",
undef, $lyr_layout->{'s2lid'}, "redist_uniq");
my ($lid_i18nc, $lid_theme, $lid_i18n);
$lid_i18nc = $style->{'layer'}->{'i18nc'};
$lid_theme = $style->{'layer'}->{'theme'};
$lid_i18n = $style->{'layer'}->{'i18n'};
my $layerid = $layer->{'s2lid'};
my @layers;
push @layers, ([ 'core' => $lyr_core->{'s2lid'} ],
[ 'i18nc' => $lid_i18nc ],
[ 'layout' => $lyr_layout->{'s2lid'} ],
[ 'i18n' => $lid_i18n ]);
if ($layer->{'type'} eq "user" && $lid_theme) {
push @layers, [ 'theme' => $lid_theme ];
}
push @layers, [ $layer->{'type'} => $layer->{'s2lid'} ];
my @layerids = grep { $_ } map { $_->[1] } @layers;
LJ::S2::load_layers(@layerids);
my %layerinfo;
# load the language and layout choices for core.
LJ::S2::load_layer_info(\%layerinfo, \@layerids);
$body .= "<a href='viewuser.bml?journal=$journal'>Raw</a> | ";
$body .= "<a href='viewuser.bml?journal=$journal&as=theme'>As Theme</a>\n";
$body .= "<p><textarea rows='40' cols='60' wrap='off'>";
$body .= "# for layout: $lyr_layout->{'s2lid'} ($lyr_layout->{'uniq'})\n";
if ($GET{'as'} eq "") {
$body .= "layerinfo type = user;\n\n";
} elsif ($GET{'as'} eq "theme") {
$body .= "layerinfo type = theme;\nlayerinfo name = \"\";\n\n";
}
foreach my $prop (S2::get_properties($lyr_layout->{'s2lid'}))
{
$prop = S2::get_property($lyr_core->{'s2lid'}, $prop)
unless ref $prop;
next unless ref $prop;
next if $prop->{'noui'};
my $name = $prop->{'name'};
my $type = $prop->{'type'};
# figure out existing value (if there was no user/theme layer)
my $existing;
foreach my $lid (reverse @layerids) {
next if $lid == $layerid;
$existing = S2::get_set($lid, $name);
last if defined $existing;
}
if (ref $existing eq "HASH") { $existing = $existing->{'as_string'}; }
my $val = S2::get_set($layerid, $name);
my $had_override = defined $val;
$val = $existing unless $had_override;
if (ref $val eq "HASH") { $val = $val->{'as_string'}; }
next if $GET{'as'} eq "" && ! $had_override;
next if $GET{'as'} eq "theme" && $type ne "Color";
if ($prop->{'type'} eq "int") {
$val = int($val);
} elsif ($prop->{'type'} eq "bool") {
$val = $val ? "true" : "false";
} else {
$val =~ s/[\\\$\"]/\\$&/g;
$val = "\"$val\"";
}
$body .= "set $name = $val;\n";
}
$body .= "</textarea>";
return;
}
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?>

174
livejournal/htdocs/delcomment.bml Executable file
View File

@@ -0,0 +1,174 @@
<?_info
nocache=>1
_info?><?_code
{
use strict;
use vars qw(%GET %POST);
use vars qw($body);
my $jsmode = $GET{mode} eq "js";
$body = "";
my $error = sub {
if ($jsmode) {
BML::finish();
return "alert('" . LJ::ejs($_[0]) . "'); 0;";
}
$body = "<?h1 $ML{'Error'} h1?><?p $_[0] p?>";
return;
};
my $bad_input = sub {
return $error->("Bad input: $_[0]") if $jsmode;
$body = LJ::bad_input($_[0]);
return;
};
LJ::set_active_crumb('delcomment');
my $remote = LJ::get_remote();
return $bad_input->($ML{'error.noremote'})
unless $remote;
return $error->("Missing parameters.") unless $GET{'journal'} ne "" && $GET{'id'};
# $u is user object of journal that owns the talkpost
my $u = LJ::load_user($GET{'journal'});
return $bad_input->($ML{'error.nojournal'})
unless $u;
# can't delete if you're suspended
return $bad_input->($ML{'.error.suspended'})
if $remote->{statusvis} eq 'S';
return $error->($LJ::MSG_READONLY_USER) if LJ::get_cap($u, "readonly");
my $dbcr = LJ::get_cluster_def_reader($u);
return $error->($ML{'error.nodb'})
unless $dbcr;
# $tp is a hashref of info about this individual talkpost row
my $tpid = $GET{'id'} >> 8;
my $tp = $dbcr->selectrow_hashref("SELECT jtalkid AS 'talkid', nodetype, state, " .
"nodeid AS 'itemid', parenttalkid, journalid, posterid " .
"FROM talk2 ".
"WHERE journalid=? AND jtalkid=?",
undef, $u->{'userid'}, $tpid);
return $bad_input->($ML{'.error.nocomment'})
unless $tp;
return $bad_input->($ML{'.error.invalidtype'})
unless $tp->{'nodetype'} eq 'L';
return $bad_input->($ML{'.error.alreadydeleted'})
if $tp->{'state'} eq "D";
# get username of poster
$tp->{'userpost'} = LJ::get_username($tp->{'posterid'});
# userid of user who posted journal entry
my $jposterid = $dbcr->selectrow_array("SELECT posterid FROM log2 WHERE " .
"journalid=? AND jitemid=?",
undef, $u->{'userid'}, $tp->{'itemid'});
my $jposter = LJ::load_userid($jposterid);
# can $remote delete this comment?
unless (LJ::Talk::can_delete($remote, $u, $jposter, $tp->{'userpost'})) {
my $err = $u->{'journaltype'} eq 'C' ? $ML{'.error.cantdelete.comm'} : $ML{'.error.cantdelete'};
return $error->($err);
}
my $can_manage = LJ::can_manage($remote, $u);
# can ban if can manage and the comment is by someone else and not anon
my $can_ban = $can_manage && $tp->{'posterid'}
&& $remote && $remote->{'userid'} != $tp->{'posterid'};
my $can_delthread = $can_manage || $jposterid == $remote->{userid};
### perform actions
if (LJ::did_post() && $POST{'confirm'}) {
# mark this as spam?
LJ::Talk::mark_comment_as_spam($u, $tp->{talkid})
if $POST{spam};
# delete entire thread? or just the one comment?
if ($POST{delthread} && $can_delthread) {
# delete entire thread ...
LJ::Talk::delete_thread($u, $tp->{'itemid'}, $tpid);
} else {
# delete single comment...
# if it's screened, unscreen it first to properly adjust
# replycount and the hasscreened logprop
LJ::Talk::unscreen_comment($u, $tp->{'itemid'}, $tpid)
if $tp->{'state'} eq 'S';
# now do the deletion
my $num = LJ::delete_comments($u, "L", $tp->{'itemid'}, $tpid);
LJ::replycount_do($u, $tp->{'itemid'}, "decr", $num);
LJ::Talk::update_commentalter($u, $tp->{'itemid'});
}
# ban the user, if selected
my $msg;
if ($POST{'ban'} && $can_ban) {
LJ::set_rel($u->{'userid'}, $tp->{'posterid'}, 'B');
$msg = BML::ml('.success.andban', { 'user' => LJ::ljuser($tp->{'userpost'}) });
}
$msg ||= $ML{'.success.noban'};
$msg .= "<?p $ML{'.success.spam'} p?>" if $POST{spam};
if ($jsmode) {
BML::finish();
return "1;";
} else {
$body = "<?h1 $ML{'.success.head'} h1?><?p $msg p?>";
return;
}
}
### show confirmation form
$body .= "<?h1 $ML{'.confirm.head'} h1?>";
$body .= "<?p $ML{'.confirm.body'} p?>";
$body .= "<form method='post' action='delcomment.bml?";
$body .= "journal=$u->{'user'}&id=$GET{'id'}'>\n";
$body .= "<?standout ";
$body .= "<div align='center' style='margin: 8px'>" . LJ::html_submit('confirm', $ML{'.confirm.submit'}) . "</div>\n";
if ($can_ban) {
$body .= "<div>" . LJ::html_check({ 'type' => 'check', 'name' => 'ban', 'id' => 'ban' });
$body .= "<label for='ban'>";
$body .= BML::ml('.confirm.banuser', { 'user' => LJ::ljuser($tp->{'userpost'}) });
$body .= "</label></div>";
}
if ($tp->{'posterid'} != $remote->{'userid'}) { # Despite the idea of natural selection, don't let users report their own comments as spam
$body .= "<div>" . LJ::html_check({name => 'spam', id => 'spam'});
$body .= "<label for='spam'>$ML{'.confirm.spam'}</label></div>";
}
if ($can_delthread) {
$body .= "<div>" . LJ::html_check({name => 'delthread', id => 'delthread'});
$body .= "<label for='delthread'>$ML{'.confirm.delthread'}</label></div>";
}
$body .= " standout?>";
if ($can_manage) {
my $msg = BML::ml('.changeoptions', { 'link' =>
"<a href='/editinfo.bml?authas=$u->{'user'}'>$ML{'/editinfo.bml.title'}</a>" });
$body .= "<?p $msg p?>";
}
$body .= "</form>\n";
return;
}
_code?><?page
title=><?_ml .title _ml?>
body=><?_code return $body; _code?>
page?><?_c <LJDEP>
link: htdocs/editinfo.bml
post: htdocs/delcomment.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,85 @@
<?page
title=>Users on this server
body<=
<?_code
{
use strict;
use vars qw(%GET);
return "Not a dev server." unless $LJ::IS_DEV_SERVER;
my $pagesize = 250;
$GET{'page'} ||= 1;
my $page = $GET{'page'} - 1;
my $offset = $page * $pagesize;
my $ret = "";
my $dbr = LJ::get_db_reader();
my %ss;
my $orderby = $GET{'sort'} eq 'user' ? "user" :
$GET{'sort'} eq 'jt' ? "journaltype,user" :
$GET{'sort'} eq 'sv' ? "statusvis,user" : "userid";
my $pagesizeplus1 = $pagesize + 1;
my $h = $dbr->prepare("SELECT userid,user,journaltype,statusvis FROM user ORDER BY $orderby LIMIT $offset,$pagesizeplus1");
$h->execute;
my @userids;
while (my $row = $h->fetchrow_hashref) {
$ss{$row->{'userid'}} = $row;
push @userids, $row->{'userid'};
}
my $islastpage = 1;
if (scalar(@userids) > $pagesize) {
pop @userids;
$islastpage = 0;
}
my $inclause = join (",", @userids);
$h = $dbr->prepare("SELECT * FROM reluser WHERE userid IN ($inclause)");
$h->execute;
while (my $row = $h->fetchrow_hashref) {
push @{$ss{$row->{'userid'}}->{'reluser'}}, $row;
}
# sort bar
my $pagearg = $GET{'page'} > 1 ? "&amp;page=$GET{'page'}" : "";
my $pagearg2 = $GET{'page'} > 1 ? "?page=$GET{'page'}" : "";
$ret .= "<?p <strong>Sort by:</strong> [<a href='userlist.bml$pagearg2'> User ID
</a>|<a href='userlist.bml?sort=user$pagearg'> Username
</a>|<a href='userlist.bml?sort=jt$pagearg'> Journal Type
</a>|<a href='userlist.bml?sort=sv$pagearg'> Status Vis
</a>] p?>";
# page bar
my $next = $page + 2;
$pagearg = $page > 1 ? "&amp;page=$page" : "";
$ret .= "<?p [" if $page || !$islastpage;
$ret .= "<a href='userlist.bml?sort=$GET{'sort'}$pagearg'> Previous Page </a>" if $page;
$ret .= "|" if $page && !$islastpage;
$ret .= "<a href='userlist.bml?sort=$GET{'sort'}&amp;page=$next'> Next Page </a>" if !$islastpage;
$ret .= "] p?>" if $page || !$islastpage;
$ret .= "<br /><br /> <table width='100%' border='1'>
<tr valign='bottom'><td><b>User ID</b></td><td><b>Username</b></td>
<td><b>T/S</b></td><td><b>Reluser Edges</b></td></tr>";
foreach my $userid (@userids) {
my $hr = $ss{$userid};
$ret .= "<tr><td><b>$userid</b></td><td>";
$ret .= LJ::ljuser($hr->{'user'}, {type=> $hr->{'journaltype'}});
$ret .= "</td><td><b>$hr->{'journaltype'}</b>/<b>$hr->{'statusvis'}</b></td><td>";
if ($hr->{'reluser'} && scalar(@{$hr->{'reluser'}})) {
my $c = 0;
foreach my $t (sort { $a->{'type'} cmp $b->{'type'} }
@{$hr->{'reluser'}}) {
$ret .= "; " if $c;
$ret .= "<b>$t->{'type'}</b>/";
$ret .= LJ::ljuser($ss{$t->{'targetid'}}->{'user'}, {type=>
$ss{$t->{'targetid'}}->{'journaltype'}});
$c = 1;
}
}
$ret .= "</td></tr>";
}
$ret .= "</table>";
return $ret;
}
_code?>
page?>

380
livejournal/htdocs/directory.bml Executable file
View File

@@ -0,0 +1,380 @@
<?_code $head = ""; _code?><?page
title=>Search Results
body<=
<?_code
return LJ::server_down_html() if ($LJ::SERVER_DOWN);
LJ::set_active_crumb('searchregion');
my @errors = ();
my @filters = LJ::Dir::validate(\%FORM, \@errors);
return LJ::bad_input(@errors) if @errors;
my $dbr = LJ::get_db_reader();
# the common case (small installations), people want the directory
# to just work. so we'll support that by default. but on big sites,
# you don't want the directory ever getting near the other databases,
# so we provide a flag to make sure the code can't.
my @extraroles = ("slave", "master");
if ($LJ::DIRECTORY_SEPARATE) { @extraroles = (); }
my $dbdir = LJ::get_dbh("directory", @extraroles);
return "Directory database not available." unless $dbdir;
my @matches;
my %info;
my $ret = "";
if (BML::get_query_string() eq "")
{
LJ::load_codes({ state => \%state, country => \%country });
$ret = "";
$ret .= "<?h1 $ML{'.browse.usa.title'} h1?><?p $ML{'.browse.usa.desc'} p?>\n";
$ret .= "<form action='get' name=\"stateForm\"><p align='center'>";
$ret .= "<script language=\"JavaScript\" type='text/javascript'><!--\n document.write('<input name=\"s\" type=\"text\" size=\"30\"><br />'); \n// --></script>\n";
$ret .= "<script language=\"JavaScript\" type='text/javascript'><!--\nfunction updateStatus (text) { self.status = text; document.stateForm.s.value=text; return true; } \n// --></script>\n";
$ret .= "<img alt='US Map' src=\"$LJ::IMGPREFIX/us_map.gif\" width='489' height='315' border='0' usemap=\"#state_test\" ismap='ismap' /></p></form><map name='state_test' id='state_test'>\n";
$sth = $dbr->prepare("SELECT statcat, statkey, statval FROM stats WHERE statcat IN ('country', 'stateus')");
$sth->execute;
while ($_ = $sth->fetchrow_hashref) {
$count{$_->{'statcat'}}->{$_->{'statkey'}} = $_->{'statval'};
}
my @shapes = (
"1,235,1,309,108,309,108,235,18,235,1,235", "AK",
"328,196,328,236,355,235,345,195,328,196,328,196", "AL",
"267,182,272,215,294,216,293,206,300,182,267,182,267,182", "AR",
"86,162,72,205,113,225,124,167,86,162,86,162", "AZ",
"14,86,6,110,22,166,54,198,69,189,29,123,38,90,14,86,14,86", "CA",
"137,122,133,160,191,169,191,128,137,122,137,122", "CO",
"444,91,443,99,456,92,456,88,444,91,444,91", "CT",
"445,158,460,158,460,168,445,158", "DC",
"428,122,433,134,436,132,430,121,428,122", "DE",
"450,126,464,135", "DE",
"335,240,335,244,371,242,391,259,410,293,414,279,390,238,335,240,335,240", "FL",
"352,194,366,234,388,233,389,216,364,192,352,194,352,194", "GA",
"119,269,185,312", "HI",
"248,101,254,126,289,126,286,97,248,101,248,101", "IA",
"86,24,73,90,114,99,118,76,100,72,86,24,86,24", "ID",
"302,111,293,135,313,162,321,147,316,111,302,111,302,111", "IL",
"326,119,328,154,344,143,343,114,326,119,326,119", "IN",
"199,140,196,167,257,170,254,141,199,140,199,140", "KS",
"325,172,324,161,341,160,348,148,366,149,367,164,325,172,325,172", "KY",
"274,224,277,255,307,254,307,244,290,244,291,221,274,224,274,224", "LA",
"471,79,488,88", "MA",
"442,82,442,89,458,84,464,89,466,87,457,79,442,82,442,82", "MA",
"465,142,483,153", "MD",
"397,128,426,122,432,135,437,136,431,142,419,138,420,128,411,128,409,126,397,129,397,128", "MD",
"462,27,457,52,461,64,476,38,469,25,462,27,462,27", "ME",
"309,56,361,61,359,107,331,110,309,56,309,56", "MI",
"243,36,250,92,277,92,268,65,283,46,243,36,243,36", "MN",
"260,134,267,173,308,173,283,133,260,134,260,134", "MO",
"322,196,321,240,299,237,301,204,322,196,322,196", "MS",
"96,22,111,64,176,73,180,33,96,22,96,22", "MT",
"388,171,374,181,415,186,423,166,388,171,388,171", "NC",
"189,33,186,59,240,63,236,36,189,33,189,33", "ND",
"184,104,182,119,200,121,200,134,248,135,237,108,184,104,184,104", "NE",
"453,51,459,74,449,79,450,61,452,60,452,51,453,51,453,51", "NH",
"435,27,452,36", "NH",
"432,102,431,109,436,114,431,121,437,125,441,111,437,111,438,103,432,102,432,102", "NJ",
"132,170,125,221,176,227,180,174,132,170,132,170", "NM",
"45,93,37,122,72,173,82,102,45,93,45,93", "NV",
"433,59,439,77,440,99,430,98,429,91,391,100,401,87,417,78,419,63,433,59", "NY",
"450,99,440,106,445,109,455,100,450,99", "NY",
"379,112,350,116,352,139,368,143,381,127,379,112,379,112", "OH",
"186,172,186,175,212,177,214,201,259,207,259,174,186,172,186,172", "OK",
"27,42,13,75,64,91,72,51,27,42,27,42", "OR",
"386,106,388,125,428,117,425,99,386,106,386,106", "PA",
"421,229,485,260", "PR",
"472,100,482,110", "RI",
"458,86,457,94,461,89,458,86", "RI",
"375,191,395,210,410,193,375,191,375,191", "SC",
"187,69,183,97,240,101,241,71,187,69,187,69", "SD",
"315,180,311,190,355,188,372,172,315,180,315,180", "TN",
"188,180,183,235,151,230,174,258,184,250,202,254,223,292,242,263,266,252,266,216,204,205,207,181,188,180,188,180", "TX",
"97,103,86,153,126,160,131,120,112,119,114,106,97,103,97,103", "UT",
"411,135,383,163,425,158,411,135,411,135", "VA",
"454,272,481,307", "VI",
"416,34,430,43", "VT",
"437,58,442,80,446,79,447,54,437,58,437,58", "VT",
"41,6,82,17,75,45,34,37,41,6,41,6", "WA",
"282,62,283,81,293,87,297,103,313,102,308,66,282,62,282,62", "WI",
"385,133,374,148,383,156,401,133,385,133,385,133", "WV",
"126,73,122,111,174,120,178,80,126,73,126,73", "WY",
);
while (my ($coords, $state) = splice(@shapes, 0, 2))
{
next unless ($count{'stateus'}->{$state});
my $shape = "poly";
if ($coords =~ /^[^,]+,[^,]+,[^,]+,[^,]+$/) { $shape = "RECTANGLE"; }
$ret .= "<area shape='$shape' alt='$state' coords=\"$coords\" href=\"/directory.bml?loc_cn=US&amp;loc_st=$state&amp;opt_sort=ut\" onmouseover=\"updateStatus('";
$ret .= BML::eall($state{$state});
$ret .= " - ";
$ret .= ($count{'stateus'}->{$state}+0);
$s = $count{'stateus'}->{$state} != 1 ? "s" : "";
$ret .= " Journal$s'); return true;\" onmouseout=\"updateStatus(''); return true;\" />\n";
}
$ret .= "</map>\n";
# by country
$ret .= "<?h1 $ML{'.browse.country.title'} h1?><?p $ML{'.browse.country.desc'} p?>";
$ret .= "<table style='margin-left: 20px' cellpadding='5'><tr valign='top'><td align='left'><ul>";
my $total = scalar(keys %{$count{'country'}});
my $count = 0;
my $col = 0;
foreach (sort { $country{$a} cmp $country{$b} } keys %{$count{'country'}})
{
$count++;
$ret .= "<li><a href=\"/directory.bml?loc_cn=$_&amp;opt_sort=ut\">$country{$_}</a> <i>($count{'country'}->{$_})</i></li>\n";
if ($col==0 && $count > ($total/2)) { $ret .= "</ul></td><td align='left'><ul>"; $col = 1; }
}
$ret .= "</ul></td></tr></table>\n";
return $ret;
}
my $remote = LJ::get_remote();
unless (LJ::check_priv($remote, "betatest", "directory") ||
LJ::get_cap($remote, "directory") ||
(@filters == 1 && $filters[0] eq "int" && $GET{'opt_format'} eq "simple"))
{
return $ML{'.error.accounttype'};
}
unless (LJ::Dir::do_search($dbr, $dbdir, \%FORM, \@matches, \%info)) {
return "<?h1 $ML{'Error'} h1?><?p $ML{'Error'}: $info{'errmsg'} p?>";
}
# opt_format and opt_sort might've been modified by do_search,
# filling in appropriate defaults based on other parameters
my $opt_format = $FORM{'opt_format'};
my $opt_sort = $FORM{'opt_sort'};
$info{'allwhat'} ||= "users";
if ($info{'searching'}) {
my ($uri, $args) = (BML::get_uri(), BML::get_query_string());
$uri .= '?' . $args if $args;
$head .= "<meta http-equiv='Refresh' content='3;URL=" . LJ::ehtml($LJ::SITEROOT . $uri) . "' />\n";
return "<center><b>$ML{'.search.title'}<img src='$LJ::IMGPREFIX/searchingdots.gif' alt='...' width='18' height='12' /></b><p>$ML{'.search.monkey'}</p></center>";
}
if ($POST{'com_do'} || $GET{'com_do'}) {
$ret .= "<a href=\"/community/search.bml\">&lt;&lt; $ML{'.search.new'}</a><p>\n";
} else {
$ret .= "<a href=\"/directorysearch.bml\">&lt;&lt; $ML{'.search.new'}</a><p>\n";
}
unless (@{$info{'english'}}) {
push @{$info{'english'}}, "wish to be listed in the public directory";
}
$ret .= "Below are all $info{'allwhat'} that ";
my $count_preds = @{$info{'english'}};
### remove consecutive "are"s
my $last_are = 0;
foreach (@{$info{'english'}}) {
if (/^are /) {
if ($last_are) {
s/^are //;
}
$last_are = 1;
} else {
$last_are = 0;
}
}
my $last = pop @{$info{'english'}};
if (@{$info{'english'}}) {
$ret .= join(", ", map { LJ::ehtml($_) } @{$info{'english'}}) . ", and ";
}
$ret .= LJ::ehtml($last) . ".\n";
if ($info{'overflow'}) {
$ret .= BML::ml(".search.overflow", {'count' => $info{'count'}});
}
if ($count_preds == 1 && $GET{'int_like'}) {
my $intid = $dbr->selectrow_array("SELECT intid FROM interests WHERE interest=?",
undef, $GET{'int_like'});
LJ::run_hooks("interests_bml", {
'intid' => $intid,
'int' => $interest,
'ret' => \$ret,
}) if $intid;
}
########## make the navcrap
my $navcrap;
$navcrap .= "<?standout <CENTER><FONT FACE=\"Arial,Helvetica\" SIZE=-1><B>".BML::ml(".navcrap.matches", {'count' => $info{'count'}})."</B>";
if ($info{'count'}) {
if ($info{'pages'} > 1) {
$navcrap .= "<BR>";
$navcrap .= BML::ml(".navcrap.xofy", {'curpage' => $info{'page'}, 'totpages' => $info{'pages'}, 'reca' => $info{'first'}, 'recb' => $info{'last'}});
$navcrap .= "<BR>";
my $left = "<B>&lt;&lt;</B>";
if ($info{'page'} > 1) { $left = "<A HREF=\"" . BML::self_link({ 'page' => $info{'page'}-1 }) . "\">$left</A>"; }
my $right = "<B>&gt;&gt;</B>";
if ($info{'page'} < $info{'pages'}) { $right = "<A HREF=\"" . BML::self_link({ 'page' => $info{'page'}+1 }) . "\">$right</A>"; }
$navcrap .= $left . " ";
for (my $i=1; $i<=$info{'pages'}; $i++) {
my $link = "[$i]";
if ($i != $info{'page'}) { $link = "<A HREF=\"" . BML::self_link({ 'page' => $i }) . "\">$link</A>"; }
else { $link = "<FONT SIZE=+1><B>$link</B></FONT>"; }
$navcrap .= "$link ";
}
$navcrap .= "$right";
}
$navcrap .= "</FONT></CENTER> standout?>\n";
} else {
$navcrap .= "</CENTER> standout?>\n";
}
####### end navcrap
$ret .= $navcrap . "<P>";
unless ($info{'count'}) { return $ret; }
if ($opt_sort eq "loc") {
LJ::load_codes({ state => \%state, country => \%country });
}
if ($opt_format eq "simple")
{
my $showloc = $GET{'opt_sort'} eq "loc" ? 1 : 0;
my %last = ();
$ret .= "<ul>\n";
foreach my $rec (@matches)
{
if ($showloc) {
if ($last{'country'} ne $rec->{'country'} ||
$last{'state'} ne $rec->{'state'} ||
$last{'city'} ne $rec->{'city'}) {
foreach (qw(country state city)) { $last{$_} = $rec->{$_}; }
my $country = $country{$rec->{'country'}};
my ($state, $city);
if ($rec->{'state'}) {
$state = ", " . ($rec->{'country'} eq "US" ? $state{$rec->{'state'}} : $rec->{'state'});
}
if ($rec->{'city'}) {
$city = ", $rec->{'city'}";
}
$ret .= "<?h1 $country$state$city h1?><BR>";
}
}
$ret .= "<a href=\"/userinfo.bml?user=$rec->{'user'}\">";
if ($rec->{'journaltype'} eq "C") {
$ret .= "<img border='0' src=\"$LJ::IMGPREFIX/community.gif\" width='16' height='16' align='absmiddle'>";
} else {
$ret .= "<img border='0' src=\"$LJ::IMGPREFIX/userinfo.gif\" width='17' height='17' align='absmiddle'>";
}
$ret .= "</a> ";
$ret .= "<a href=\"/users/$rec->{'user'}/\">$rec->{'user'}</A> - <b>" . LJ::ehtml($rec->{'name'}) . "</b>, <font size='-1' face=\"Arial\"><i>Updated ";
$ret .= LJ::ago_text($rec->{'secondsold'});
$ret .= "</i></font><br />\n";
}
$ret .= "</ul>\n";
}
if ($opt_format eq "com")
{
$ret .= "<TABLE CELLSPACING=3>\n";
$ret .= "<TR><TD>&nbsp;</TD><TD><B>$ML{'.user'}</B></TD><TD><B>$ML{'.community'}</B></TD><TD><B>$ML{'.open'}</B></TD><TD><B>$ML{'.post'}</B></TD></TR>\n";
foreach my $rec (@matches)
{
$ret .= "<TR VALIGN=TOP>";
$ret .= "<TD NOWRAP><A HREF=\"/userinfo.bml?user=$rec->{'user'}\">";
if ($rec->{'journaltype'} eq "C") {
$ret .= "<IMG BORDER=0 SRC=\"$LJ::IMGPREFIX/community.gif\" WIDTH=16 HEIGHT=16 ALIGN=ABSMIDDLE>";
} else {
$ret .= "<IMG BORDER=0 SRC=\"$LJ::IMGPREFIX/userinfo.gif\" WIDTH=17 HEIGHT=17 ALIGN=ABSMIDDLE>";
}
$ret .= "</A></TD>";
$ret .= "<TD><B><A HREF=\"/community/$rec->{'user'}/\">$rec->{'user'}</A></B></TD>";
$ret .= "<td>" . LJ::ehtml($rec->{'name'}) . "</td>";
my $color;
if ($rec->{'membership'} eq "open") { $color = "green"; } else { $color = "red"; }
$ret .= "<TD ALIGN=CENTER><IMG SRC=\"$LJ::IMGPREFIX/dot_$color.gif\" WIDTH=14 HEIGHT=14></TD>";
if ($rec->{'postlevel'} eq "members") { $color = "green"; } else { $color = "red"; }
$ret .= "<TD ALIGN=CENTER><IMG SRC=\"$LJ::IMGPREFIX/dot_$color.gif\" WIDTH=14 HEIGHT=14></TD>";
$ret .= "</TR>";
}
$ret .= "</TABLE>\n";
}
if ($opt_format eq "pics")
{
my $showloc = $GET{'opt_sort'} eq "loc" ? 1 : 0;
my %last = ();
my %pic;
my @picids = map { [$_, $_->{'defaultpicid'}] } @matches;
LJ::load_userpics(\%pic, \@picids);
my $count = 0;
my $pos = 0;
my $more_to_show = 0;
$ret .= "<TABLE CELLPADDING=3>\n";
foreach my $rec (@matches)
{
if ($pos==5) { $ret .= "</TR>\n"; }
$pos++; $pos %= 5;
if ($showloc) {
if ($last{'country'} ne $rec->{'country'} ||
$last{'state'} ne $rec->{'state'} ||
$last{'city'} ne $rec->{'city'}) {
foreach (qw(country state city)) { $last{$_} = $rec->{$_}; }
my $country = $country{$rec->{'country'}};
my ($state, $city);
if ($rec->{'state'}) {
$state = ", " . ($rec->{'country'} eq "US" ? $state{$rec->{'state'}} : $rec->{'state'});
}
if ($rec->{'city'}) {
$city = ", $rec->{'city'}";
}
if ($pos > 1) { $ret .= "</TR>"; $pos = 1; }
$ret .= "</TABLE>";
$ret .= "<?h1 $country$state$city h1?><BR>";
$ret .= "<TABLE CELLPADDING=3>\n";
}
}
if ($pos==1) { $ret .= "<TR ALIGN=CENTER VALIGN=BOTTOM>\n"; }
my $picid = $rec->{'defaultpicid'};
my $updateago = LJ::ago_text($rec->{'secondsold'});
my $img;
if ($picid) {
$img = "<IMG SRC=\"$LJ::USERPIC_ROOT/$picid/$rec->{'userid'}\" ALT=\"$_->{'user'}\" WIDTH=$pic{$picid}->{'width'} HEIGHT=$pic{$picid}->{'height'} BORDER=0><BR>";
}
$ret .= "<TD>";
$ret .= $img;
$ret .= LJ::ljuser($rec->{'user'});
$ret .= "<BR><FONT SIZE=-1><B>$Ml{'.update'} </B> $updateago</FONT></TD>\n";
}
$ret .= "</TR></TABLE>\n";
}
if ($info{'pages'} > 1) { $ret .= $navcrap; }
return $ret;
_code?>
<=body
head<=
<meta name="robots" content="noindex,nofollow" />
<?_code return $head; _code?>
<=head
page?>

View File

@@ -0,0 +1,205 @@
<?_info
localblocks<=
crit<=
{FpRs}<tr bgcolor='<?emcolor?>'>
<td align='left' colspan='2'><b>%%name%%</b>
</tr>
<tr align='left'><td>&nbsp;</td><td>%%form%%</td></tr>
<=crit
bar<=
{FpRs}<tr bgcolor='<?emcolor?>' align='left'>
<td>&nbsp;</td>
<td><b>%%name%%</b></td>
</tr>
<tr align='left'><td>&nbsp;</td><td>%%form%%</td></tr>
<=bar
<=localblocks
_info?><?page
title=>Directory Search
body<=
<?_code
LJ::set_active_crumb('advsearch');
_code?>
<?h1 Search Directory h1?>
<?p
Fill in criteria below that you'd like to find user journals by. The results returned are the intersection of all search criteria. In other words, each restriction means "AND", not "OR".
p?>
<center>
<form style='margin-top: 1.5em' action='/directory.bml' method='get'>
<table cellpadding='4' cellspacing='0' border='0'>
<!--- location --->
<?crit
name=>By Location
code=>s_loc
form<=
<table>
<tr><td align='right'>Country:</td><td align='left'>
<?_code
LJ::load_codes({ "country" => \%countries });
return LJ::html_select({
'name' => 'loc_cn',
}, "", "", map { $_, $countries{$_} } "US", sort { $countries{$a} cmp $countries{$b} } keys %countries);
_code?>
</td></tr>
<tr><td align='right'>State/Province:</td><td align='left'><input name="loc_st" /></td></tr>
<tr><td align='right'>City:</td><td align='left'><input name="loc_ci" /></td></tr>
</table>
<=form
crit?>
<!---- update time ----->
<?crit
name=>By Journal Update Time
code=>s_ut
form<=
Updated in last
<select name="ut_days">
<option value="">-------</option>
<option value="1">day</option>
<option value="7">week</option>
<option value="30">month</option>
</select>
<=form
crit?>
<!---- age ----->
<?crit
name=>By Age
code=>s_age
form<=
Between
<input name="age_min" size='3' maxlength='3' /> and
<input name="age_max" size='3' maxlength='3' /> years old.
<=form
crit?>
<?_c DISABLED because of privacy policy. add later when 'public gender' option
<!---- gender ----->
<?crit
name=>By Gender
code=>s_gen
form<=
User is: <select name="gen_sel">
<option></option>
<option value="M">Male</option>
<option value="F">Female</option>
</select>
<=form
crit?>
_c?>
<!---- interest ----->
<?crit
name=>By Interest
code=>s_int
form<=
User likes: <input name="int_like" size='30' />
<=form
crit?>
<!---- has friend ----->
<?crit
name=>Has Friend
code=>s_fr
form<=
User lists <input name="fr_user" size='15' maxlength='15' /> as a friend.
<=form
crit?>
<!---- friend of ----->
<?crit
name=>Friend of
code=>s_fro
form<=
<input name="fro_user" size='15' maxlength='15' /> lists user as a friend.
<=form
crit?>
<?_c dead for now
<!---- client used ----->
<?crit
name=>Client Usage
code=>s_client
form<=
Has used update client named: <input name="client_match" SIZE='20' />
<=form
crit?>
_c?>
<?_c no index on this yet. need a userprop boolean.
<!----- other ------>
<?bar
name=>Other Criteria
form<=
<input type='checkbox' name="s_withpic" value='1' /> Only users with pictures
<=form
bar?>
_c?>
<!---- output formatting ----->
<?bar
name=>Display Options
form<=
<table>
<tr>
<td align='right'>
Output Format:
</td><td>
<select name="opt_format">
<option value="pics">By Picture</option>
<option value="simple">Simple</option>
</select>
</td></tr>
<tr>
<td align='right'>
Sorting Method:
</td><td>
<select name="opt_sort">
<option value="ut">Update Time</option>
<option value="user">User Name</option>
<option value="loc">Location</option>
</select>
</td></tr>
<tr>
<td align='right'>
Records per page:
</td><td>
<select name="opt_pagesize">
<option value="25">25</option>
<option value="50">50</option>
<option value="100" selected='selected'>100</option>
<option value="200">200</option>
</select>
</td></tr>
</table>
<=form
bar?>
<!--- submit --->
<tr bgcolor='<?emcolor?>'><td colspan='2' align='center'>
<input type='submit' value="Search!" />
<input type='reset' value="Clear Form" />
</td></tr>
</table>
</form>
</center>
<=body
page?><?_c <LJDEP>
form: htdocs/directory.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,3 @@
This file exists to make sure the directory is created.
Other things (like doc/raw/build/generate.pl) depend
on this directory existing, and populating into it.

886
livejournal/htdocs/editinfo.bml Executable file
View File

@@ -0,0 +1,886 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
LJ::set_active_crumb('editinfo');
return;
_code?>
<?_code
{
use strict;
use vars qw(%POST %GET);
return LJ::server_down_html() if $LJ::SERVER_DOWN;
my $remote = LJ::get_remote();
return LJ::bad_input("You must be logged in to edit your info.")
unless $remote;
if ($remote->underage) {
return BML::redirect("$LJ::SITEROOT/agecheck/?s=1");
}
my $authas = $GET{'authas'} || $remote->{'user'};
my $u = LJ::get_authas_user($authas);
return LJ::bad_input("You could not be authenticated as the specified user.")
unless $u;
return $LJ::MSG_READONLY_USER if $u->readonly;
# extra arguments for get requests
my $getextra = $authas ne $remote->{'user'} ? "?authas=$authas" : '';
### user is now authenticated ###
my $dbr = LJ::get_db_reader();
my $sth;
# load user props
LJ::load_user_props($u, { use_master => 1 }, "opt_whatemailshow",
"country", "state", "city",
"zip", "icq", "aolim", "yahoo", "msn", "url",
"urlname", "gender", "jabber", "opt_blockrobots",
"opt_logcommentips",
"howhear", "opt_bdaymail", "opt_hidefriendofs",
"sidx_bdate", "sidx_loc", "mailencoding", "opt_nctalklinks",
"opt_whoscreened", "journaltitle", "journalsubtitle",
"friendspagetitle", "opt_weblogscom", "opt_stylemine",
"opt_imagelinks", "opt_getselfemail", "external_foaf_url",
"opt_showmutualfriends",
);
# to store values before they undergo normalisation
my %saved = ();
$saved{'name'} = $u->{'name'};
# clean userprops
foreach (values %$u) { LJ::text_out(\$_); }
# load and clean bio
$u->{'bio'} = LJ::get_bio($u);
$saved{'bio'} = $u->{'bio'};
LJ::text_out(\$u->{'bio'}, "force");
# load interests
my $uints = LJ::get_interests($u, { forceids => 1 });
my %interests = ();
foreach (@$uints) {
$interests{$_->[1]} = $_->[0]; # $interests{name} = intid
}
# load state and country codes
my %countries;
my %states;
LJ::load_codes({ "country" => \%countries, "state" => \%states });
###
### no post, show edit form
###
unless (LJ::did_post()) {
my $ret;
# user switcher
$ret .= "<form method='get' action='editinfo.bml'>\n";
$ret .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'} });
$ret .= "</form>\n\n";
$ret .= "<form method='post' action='editinfo.bml$getextra'>\n";
$ret .= LJ::form_auth();
# personal information
$ret .= "<?h1 $ML{'.persinfo.header'} h1?><?p $ML{'.persinfo.disclaimer'} p?>\n";
$ret .= "<table width='100%'>\n";
# name
$ret .= "<tr><td align='right' bgcolor='<?emcolor?>'>$ML{'.name.title'}</td>";
if (LJ::text_in($saved{'name'})) {
$ret .= "<td>" . LJ::html_text({ 'name' => 'name', 'value' => $u->{'name'},
'maxlength' => '50' }) . "</td></tr>\n";
} else {
$ret .= "<td>" . LJ::html_hidden('name_absent', 'yes');
$ret .= "<?inerr $ML{'.error.invalidname'} inerr?></td></tr>\n";
}
# birthday
$ret .= "<tr><td align='right' bgcolor='<?emcolor?>'>$ML{'.bday.title'}</td><td>";
my %bdpart;
if ($u->{'bdate'} =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/) {
($bdpart{'year'}, $bdpart{'month'}, $bdpart{'day'}) = ($1, $2, $3);
if ($bdpart{'year'} eq "0000") { $bdpart{'year'} = ""; }
if ($bdpart{'day'} eq "00") { $bdpart{'day'} = ""; }
}
$ret .= LJ::html_select({ 'name' => 'month', 'selected' => int($bdpart{'month'}) },
'', '', map { $_, $ML{LJ::Lang::month_long_langcode($_)} } (1..12)) . " ";
$ret .= LJ::html_text({ 'name' => 'day', 'value' => $bdpart{'day'}, 'size' => '3', 'maxlength' => '2' }) . " ";
$ret .= LJ::html_text({ 'name' => 'year', 'value' => $bdpart{'year'}, 'size' => '5', 'maxlength' => '4' });
$ret .= " ($ML{'.bday.year.opt'})";
$ret .= "</td></tr>\n";
# gender
$ret .= "<tr><td align='right' bgcolor='<?emcolor?>'>$ML{'.gender.title'}</td><td>";
$ret .= LJ::html_select({ 'name' => 'gender', 'selected' => $u->{'gender'} },
'U' => "(Unspecified)", 'M' => "Male", 'F' => "Female" );
$ret .= "</td></tr>\n";
# email
$ret .= "<tr><td align='right' bgcolor='<?emcolor?>'>$ML{'.email.title'}</td><td>";
$ret .= LJ::html_text({ 'name' => 'email', 'value' => $u->{'email'}, 'size' => '40', 'maxlength' => '50' });
$ret .= "</td></tr>\n";
# url
$ret .= "<tr><td align='right' bgcolor='<?emcolor?>'>$ML{'.webpageurl.title'}</td><td>";
$ret .= LJ::html_text({ 'name' => 'url', 'value' => $u->{'url'}, 'size' => '40', 'maxlength' => '255' });
$ret .= " ($ML{'.optional'})</td></tr>\n";
# urlname
$ret .= "<tr><td align='right' bgcolor='<?emcolor?>'>$ML{'.webpagename.title'}</td><td>";
$ret .= LJ::html_text({ 'name' => 'urlname', 'value' => $u->{'urlname'}, 'size' => '40', 'maxlength' => '255' });
$ret .= " ($ML{'.optional'})</td></tr>\n";
# chat thingies
foreach my $p (["aolim", $ML{'.chat.aolim.title'}, 28], ["icq", $ML{'.chat.icquin.title'}, 12],
["yahoo", $ML{'.chat.yahooid.title'}, 33], ["msn", $ML{'.chat.msnusername.title'}, 60],
["jabber", $ML{'.chat.jabber.title'}, 60])
{
$ret .= "<tr><td align='right' bgcolor='<?emcolor?>'>$p->[1]</td><td>";
$ret .= LJ::html_text({ 'name' => $p->[0], 'value' => $u->{$p->[0]}, 'size' => '20', 'maxlength' => $p->[2] });
$ret .= " ($ML{'.optional'})</td></tr>\n";
}
# country
$ret .= "<tr><td align='right' bgcolor='<?emcolor?>'>$ML{'.country.title'}</td><td>";
$ret .= LJ::html_select({ 'name' => 'country', 'selected' => $u->{'country'} },
'', $ML{'.country.choose'},
'US', 'United States',
map { $_, $countries{$_} } sort { $countries{$a} cmp $countries{$b} } keys %countries );
$ret .= "</td></tr>\n";
# city
$ret .= "<tr><td align='right' bgcolor='<?emcolor?>'>$ML{'.city.title'}</td><td>";
$ret .= LJ::html_text({ 'name' => 'city', 'value' => $u->{'city'}, 'size' => '20', 'maxlength' => '255' });
$ret .= "</td></tr>\n";
# state
$ret .= "<tr valign='top'><td align='right' bgcolor='<?emcolor?>'>$ML{'.state.title'}</td><td>";
$ret .= LJ::html_select({ 'name' => 'statedrop', 'selected' => $u->{'state'} },
'', "($ML{'.state.us'})",
map { $_, $states{$_} } sort { $states{$a} cmp $states{$b} } keys %states );
# other state?
$ret .= "<br />$ML{'.state.other'}: ";
$ret .= LJ::html_text({ 'name' => 'stateother', 'size' => '20', 'maxlength' => '50',
'value' => defined $states{$u->{'state'}} ? '' : $u->{'state'} });
$ret .= "</td></tr>\n";
# zip
$ret .= "<tr><td align='right' bgcolor='<?emcolor?>'>$ML{'.zip.title'}</td><td>";
$ret .= LJ::html_text({ 'name' => 'zip', 'value' => $u->{'zip'}, 'size' => '6', 'maxlength' => '5' });
$ret .= " ($ML{'.zip.usonly'})</td></tr>\n";
# text messaging
if (LJ::get_cap($u, "textmessaging"))
{
$sth = $dbr->prepare("SELECT provider, number, security FROM txtmsg WHERE userid=?");
$sth->execute($u->{'userid'});
my $tminfo = $sth->fetchrow_hashref;
foreach (values %$tminfo) { LJ::text_out(\$_); }
# text messaging
$ret .= "<tr valign='top'><td align='right' bgcolor='<?emcolor?>'>";
$ret .= LJ::help_icon('textmessage', "", " ");
$ret .= "$ML{'.tm.title'}</td><td>\n<table>\n<tr><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'use_txtmsg', 'id' => 'use_txtmsg',
'selected' => $u->{'txtmsg_status'} eq 'on' });
$ret .= "</td><td colspan='2'><b><label for='use_txtmsg'>$ML{'.tm.sec.about'}</label></b></td></tr>\n";
$ret .= "<tr><td rowspan='3'>&nbsp;</td><td>$ML{'.tm.sec.title'}</td><td>";
$ret .= LJ::html_select({ 'name' => 'txtmsg_security', 'selected' => $tminfo->{'security'} },
"all" => BML::ml(".security.visibility.anybody"),
"reg" => BML::ml(".security.visibility.regusers"),
"friends" => BML::ml(".security.visibility.friends") );
$ret .= "</td></tr>\n";
$ret .= "<tr><td>$ML{'.tm.servprov'}</td><td>";
{
my @opts = ("", "");
foreach my $p (LJ::TextMessage::providers()) {
my $info = LJ::TextMessage::provider_info($p);
push @opts, ($p, $info->{'name'});
}
$ret .= LJ::html_select({ 'name' => 'txtmsg_provider',
'selected' => $tminfo->{'provider'}, },
@opts);
}
$ret .= " ($ML{'.tm.details'})</td></tr>\n";
$ret .= "<tr><td>$ML{'.tm.phonenum'}</td><td>";
$ret .= LJ::html_text({ 'name' => 'txtmsg_number', 'value' => $tminfo->{'number'},
'size' => '15', 'maxlength' => '40' });
$ret .= "</td></tr>\n";
$ret .= "</table>\n</td></tr>\n";
}
# end personal info
$ret .= "</table>\n\n";
### User bio
$ret .= "<?h1 $ML{'.bio.header'} h1?><?p $ML{'.bio.about'} p?>";
if (LJ::text_in($saved{'bio'})) {
$ret .= "<div style='margin-left: 30px; margin-bottom: 20px'>";
$ret .= LJ::html_textarea({ 'name' => 'bio', 'rows' => '10', 'cols' => '50',
'wrap' => 'soft', 'value' => $u->{'bio'}, 'style' => "width: 90%", }) . "</div>\n";
} else {
$ret .= LJ::html_hidden('bio_absent', 'yes');
$ret .= "<?p <?inerr $ML{'.error.invalidbio'} inerr?> p?>\n";
}
### How heard Settings
unless ($u->{'howhear'}) {
$ret .= "<?h1 $ML{'.howhear.header'} h1?>\n";
$ret .= "<?p " . BML::ml(".howhear.about", { 'sitename' => $LJ::SITENAME }) . " p?>\n";
$ret .= "<div style='margin-left: 30px; margin-bottom: 20px;'>";
$ret .= LJ::html_text({ 'name' => 'howhear', 'size' => '60', 'maxlength' => '100' });
$ret .= "</div>\n\n";
}
{
### Interests
$ret .= "<?h1 $ML{'.int.header'} h1?>\n";
my @eintsl;
foreach (sort keys %interests) {
push @eintsl, $_ if LJ::text_in($_);
}
$ret .= "<?p $ML{'.int.about'} p?>";
$ret .= "<?p $ML{'.int.ex.good'} p?>";
$ret .= "<?p $ML{'.int.ex.bad'} p?>";
$ret .= "<div style='margin-left: 30px; margin-bottom: 20px;'>";
$ret .= LJ::html_textarea({ 'name' => 'interests', 'value' => join(", ", @eintsl),
'rows' => '10', 'cols' => '50', 'wrap' => 'soft' });
$ret .= "</div>\n\n";
}
### Picture Settings
$ret .= "<?h1 $ML{'.userpic.header'} h1?>\n<?p $ML{'.userpic.about'} p?>\n";
$ret .= "<p align='center'>";
if ($u->{'defaultpicid'})
{
my $picid = $u->{'defaultpicid'};
my %userpics = ();
LJ::load_userpics(\%userpics, [ $u, $picid ]);
$ret .= "<a href='editpics.bml$getextra'><img src=\"$LJ::USERPIC_ROOT/$picid/$u->{'userid'}\" width='$userpics{$picid}->{'width'}' height='$userpics{$picid}->{'height'} alt='$u->{'user'}' border='0'></a>";
} else
{
$ret .= "<i>($ML{'.userpic.none'})</i>";
}
$ret .= "</p><p>$ML{'.userpic.edit'}</p>\n\n";
###
### Journal Options
###
$ret .= "<?h1 $ML{'.settings.header'} h1?>\n";
### display options
$ret .= "<?p $ML{'.settings.about'} p?>\n";
$ret .= "<table style='margin-left: 30px; margin-bottom: 20px'>\n";
# journaltitle
$ret .= "<tr><td><b>$ML{'.settings.journal.title'} </b></td>";
$ret .= "<td>" . LJ::html_text({ 'name' => 'journaltitle', 'value' => $u->{'journaltitle'}, 'size' => '30', 'maxlength' => '80' }) . " </td></tr>\n";
# journalsubtitle
$ret .= "<tr><td><b>$ML{'.settings.journal.subtitle'} </b></td>";
$ret .= "<td>" . LJ::html_text({ 'name' => 'journalsubtitle', 'value' => $u->{'journalsubtitle'}, 'size' => '30', 'maxlength' => '80' }) . " </td></tr>\n";
$ret .= "<tr><td colspan='2'>$ML{'.settings.journal.subtitle.optional'}</td></tr>\n";
# friendspagetitle
$ret .= "<tr><td><b>$ML{'.settings.friendspage.title'} </b></td>";
$ret .= "<td>" . LJ::html_text({ 'name' => 'friendspagetitle', 'value' => $u->{'friendspagetitle'}, 'size' => '30', 'maxlength' => '80' }) . "</td></tr>\n";
$ret .= "<tr><td colspan='2'>$ML{'.settings.friendspage.title.optional'}</td></tr>\n";
$ret .= "</table>\n\n";
### privacy options
$ret .= "<?h2 $ML{'.settings.privacy.header'} h2?><?p $ML{'.settings.privacy.about'} p?>\n";
$ret .= "<table style='margin: 10px 0 20px 30px'>\n";
# allow_contactshow
$ret .= "<tr valign=middle><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'allow_contactshow', 'id' => 'allow_contactshow',
'selected' => $u->{'allow_contactshow'} ne 'N' });
$ret .= "</td><td><b><label for='allow_contactshow'>$ML{'.allowshowcontact.title'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.allowshowcontact.about'}";
{
# contactshow_sec
$ret .= "\n<p><b>$ML{'.security.header'}</b> ";
$ret .= LJ::html_select({ 'name' => 'contactshow_sec',
'selected' => $u->{'allow_contactshow'} },
"Y" => BML::ml(".security.visibility.everybody"),
"F" => BML::ml(".security.visibility.friends") );
$ret .= "</p>\n";
# opt_whatemailshow
$ret .= "<p><b>$ML{'.allowshowcontact.email'}</b>\n";
$ret .= "<div style='margin-left: 30px; margin-bottom: 20px;'>";
my $cur = $u->{'opt_whatemailshow'} || "N";
my @vals = ( ($LJ::USER_EMAIL && LJ::get_cap($u, "useremail"))
? ("B" => BML::ml(".allowshowcontact.email.both", { 'domain' => $LJ::USER_DOMAIN}),
"A" => BML::ml(".allowshowcontact.email.actual_only"),
"L" => BML::ml(".allowshowcontact.email.lj_only"),
"N" => BML::ml(".allowshowcontact.email.neither"))
: ("A" => BML::ml(".allowshowcontact.email.show"),
"N" => BML::ml(".allowshowcontact.email.no_show")));
$ret .= LJ::html_select({ 'name' => 'opt_whatemailshow', 'selected' => $cur }, @vals) . "\n";
$ret .= "<p>" . ($LJ::USER_EMAIL
? $ML{'.allowshowcontact.email.withdomainaddr'}
: $ML{'.allowshowcontact.email.withoutdomainaddr'}) . "</p>\n</div>\n";
# opt_mangleemail
$ret .= "<table style='margin-bottom: 20px;'>\n<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_mangleemail', 'id' => 'opt_mangleemail',
'selected' => $u->{'opt_mangleemail'} eq 'Y' });
$ret .= "</td><td><b><label for='opt_mangleemail'>$ML{'.mangleaddress.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.mangleaddress.about'}</td></tr>\n</table>\n";
}
$ret .= "</td></tr>\n";
# allow_infoshow
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'allow_infoshow', 'id' => 'allow_infoshow',
'selected' => $u->{'allow_infoshow'} eq 'Y' });
$ret .= "</td><td><b><label for='allow_infoshow'>$ML{'.allowshowinfo.title'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.allowshowinfo.about'}</td></tr>\n";
# opt_blockrobots
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_blockrobots', 'id' => 'opt_blockrobots',
'selected' => $u->{'opt_blockrobots'} });
$ret .= "</td><td><b><label for='opt_blockrobots'>$ML{'.blockrobots.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.blockrobots.about'}</td></tr>\n";
# opt_weblogscom
if (LJ::get_cap($u, "weblogscom")) {
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_weblogscom', 'id' => 'opt_weblogscom',
'selected' => $u->{'opt_weblogscom'} });
$ret .= "</td><td><b><label for='opt_weblogscom'>$ML{'.weblogscom.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.weblogscom.about'}</td></tr>\n";
}
# opt_showmutualfriends
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_showmutualfriends', 'id' => 'opt_showmutualfriends',
'selected' => $u->{'opt_showmutualfriends'} });
$ret .= "</td><td><b><label for='opt_showmutualfriends'>$ML{'.mutualfriends.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.mutualfriends.about'}</td></tr>\n";
# opt_hidefriendofs
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_hidefriendofs', 'id' => 'opt_hidefriendofs',
'selected' => $u->{'opt_hidefriendofs'} });
$ret .= "</td><td><b><label for='opt_hidefriendofs'>$ML{'.hidefriendof.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.hidefriendof.about'}</td></tr>\n";
# allow_getljnews
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'allow_getljnews', 'id' => 'allow_getljnews',
'selected' => $u->{'allow_getljnews'} eq 'Y' });
$ret .= "</td><td><b><label for='allow_getljnews'>$ML{'.opt_in.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.opt_in.about'}</td></tr>\n";
# opt_bdaymail
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_bdaymail', 'id' => 'opt_bdaymail',
'selected' => $u->{'opt_bdaymail'} });
$ret .= "</td><td><b><label for='opt_bdaymail'>$ML{'.bdayreminders.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.bdayreminders.about'}</td></tr>\n";
# opt_imagelinks
my ($maxwidth, $maxheight) = (0, 0);
($maxwidth, $maxheight) = ($1, $2)
if ($u->{'opt_imagelinks'} =~ m/^(\d+)\|(\d+)$/);
my $is_stock = {'320|240' => 1, '640|480' => 1, '0|0' => 1, '' => 1}->{$u->{'opt_imagelinks'}};
my $extra = $is_stock ? '' : BML::ml('.imagelinks.size.custom',
{'width' => $maxwidth, 'height' => $maxheight});
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_imagelinks_on', 'id' => 'opt_imagelinks_on',
'selected' => $u->{'opt_imagelinks'} });
$ret .= "</td><td><b><label for='opt_imagelinks_on'>$ML{'.imagelinks.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.imagelinks.about'}</td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>";
$ret .= LJ::html_select({'name' => 'opt_imagelinks', 'selected' => $u->{'opt_imagelinks'}},
'0|0', BML::ml('.imagelinks.size.all'),
'320|240', BML::ml('.imagelinks.size.small', {'width' => 320, 'height' => 240}),
'640|480', BML::ml('.imagelinks.size.medium', {'width' => 640, 'height' => 480}),
$extra ? ("$maxwidth|$maxheight", $extra) : ());
$ret .= "</td></tr>\n";
# opt_getselfemail
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_getselfemail', 'id' => 'opt_getselfemail',
'selected' => $u->{'opt_getselfemail'},
'disabled' => !LJ::get_cap($u, 'getselfemail') });
$ret .= "</td><td><b><label for='opt_getselfemail'>$ML{'.getselfemails.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.getselfemails.about'}</td></tr>\n";
# opt_showtalklinks
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_showtalklinks', 'id' => 'opt_showtalklinks',
'selected' => $u->{'opt_showtalklinks'} eq 'Y' });
$ret .= "</td><td><b><label for='opt_showtalklinks'>$ML{'.enableboards.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.enableboards.about'}";
{
# opt_whocanreply
$ret .= "\n<p><b>$ML{'.whoreply.header'}</b> ";
$ret .= LJ::html_select({ 'name' => 'opt_whocanreply', 'selected' => $u->{'opt_whocanreply'} },
"all" => BML::ml(".security.visibility.anybody"),
"reg" => BML::ml(".security.visibility.regusers"),
"friends" => BML::ml(".security.visibility.friends"));
$ret .= "</p>\n";
# opt_logcommentips
$ret .= "<p><b>$ML{'.logip.header'}</b> ";
$ret .= LJ::html_select({ 'name' => 'opt_logcommentips', 'selected' => $u->{'opt_logcommentips'} },
"N" => BML::ml(".donotlog"),
"S" => BML::ml(".logip.anon_only"),
"A" => BML::ml(".logip.always") );
$ret .= "</p>\n";
# opt_whoscreened
$ret .= "<p><b>$ML{'.screen.header'}</b> ";
$ret .= LJ::html_select({ 'name' => 'opt_whoscreened', 'selected' => $u->{'opt_whoscreened'} },
"N" => $ML{'.screen.none'},
"R" => $ML{'.screen.anon'},
"F" => ($u->{'journaltype'} eq 'C' ? $ML{'.screen.nonmembers'} : $ML{'.screen.nonfriends'}),
"A" => $ML{'.screen.all'} );
$ret .= "</p>\n";
$ret .= "<table>\n";
# opt_nctalklinks
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_nctalklinks', 'id' => 'opt_nctalklinks',
'selected' => $u->{'opt_nctalklinks'} });
$ret .= "</td><td><b><label for='opt_nctalklinks'>$ML{'.numcomments.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.numcomments.about'}</td></tr>\n";
# stylemine
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_stylemine', 'id' => 'opt_stylemine',
'value' => 1, 'selected' => $u->{'opt_stylemine'} });
$ret .= "</td><td><b><label for='opt_stylemine'>$ML{'.stylemine.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.stylemine.about'}</td></tr>\n";
# opt_gettalkemail
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_gettalkemail', 'id' => 'opt_gettalkemail',
'selected' => $u->{'opt_gettalkemail'} eq 'Y' });
$ret .= "</td><td><b><label for='opt_gettalkemail'>$ML{'.getreplies.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.getreplies.about'}</td></tr>\n";
# opt_htmlemail
$ret .= "<tr valign='middle'><td>";
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'opt_htmlemail', 'id' => 'opt_htmlemail',
'selected' => $u->{'opt_htmlemail'} eq 'Y' });
$ret .= "</td><td><b><label for='opt_htmlemail'>$ML{'.htmlemail.header'}</label></b></td></tr>\n";
$ret .= "<tr><td>&nbsp;</td><td>$ML{'.htmlemail.about'}</td></tr>\n";
$ret .= "</table>\n";
}
$ret .= "</td></tr>\n";
$ret .= "</table>\n\n";
### advanced options
$ret .= "<?h1 $ML{'.advanced.title'} h1?><?p $ML{'.advanced.about'} p?>\n";
$ret .= "<table style='margin: 10px 0 20px 30px;'>\n";
# external foaf link
$ret .= "<tr><td><b>$ML{'.foafurl.title'}</b></td><td>";
$ret .= LJ::html_text({ 'name' => 'external_foaf_url',
'value' => $u->{'external_foaf_url'}, 'size' => '40', 'maxlength' => '255' });
$ret .= "</td></tr><tr><td colspan='2'>$ML{'.foafurl.about'}</td></tr>";
# done
$ret .= "</table>\n";
### unicode options
if ($LJ::UNICODE) {
my (%old_encnames, %mail_encnames, %encodings);
LJ::load_codes({ "encoding" => \%encodings } );
LJ::load_codes({ "encname" => \%old_encnames } );
%mail_encnames = %old_encnames;
# which encodings to show? For now, we just delete utf-8 from the old encodings
# list because it doesn't make sense there.
foreach my $id (keys %encodings) {
delete $old_encnames{$id} if lc($encodings{$id}) eq 'utf-8';
}
$ret .= "<?h2 $ML{'.encoding.header'} h2?><?p $ML{'.encoding.about'} p?>\n";
$ret .= "<table style='margin: 10px 0 20px 30px;'>\n";
$ret .= "<tr><td><b>$ML{'.autotranslate.header'}</b></td>";
$ret .= "<td>" . LJ::html_select({ 'name' => 'oldenc', 'selected' => $u->{'oldenc'}},
map { $_, $old_encnames{$_} } sort keys %old_encnames ) . "</td></tr>\n";
$ret .= "<tr><td colspan='2'>" . $ML{'.autotranslate.about'} . "</td></tr>";
if ($u->{journaltype} eq 'P') {
$ret .= "<tr><td><b>$ML{'.translatemailto.header'}</b></td>";
$ret .= "<td>" . LJ::html_select({ 'name' => 'mailencoding', 'selected' => $u->{'mailencoding'}},
map { $_, $mail_encnames{$_} } sort keys %mail_encnames ) . "</td></tr>\n";
$ret .= "<tr><td colspan='2'>$ML{'.translatemailto.about'}</td></tr>";
}
$ret .= "</table>\n";
}
### let them un-ban users if they've banned users
my $banned = LJ::load_rel_user($u, 'B');
if ($banned && @$banned) {
my $us = LJ::load_userids(@$banned);
$ret .= "<?h1 $ML{'.unbanusers.header'} h1?><?p $ML{'.unbanusers.about'} p?>\n";
$ret .= "<div style='margin: 10px 0 20px 30px'>\n";
foreach (@$banned) {
my $bu = $us->{$_};
next unless $bu;
$ret .= LJ::html_check({ 'type' => 'check', 'name' => 'unban', 'id' => "unban-$bu->{'user'}",
'value' => $bu->{'userid'} });
$ret .= " <label for='unban-$bu->{'user'}'>$bu->{'user'}</label><br />\n";
}
$ret .= "</div>\n";
}
# ending submit block
$ret .= "<?h1 $ML{'.finished.header'} h1?><?p $ML{'.finished.about'} p?>\n";
$ret .= "<?standout " . LJ::html_submit(undef, $ML{'.finished.save_button'}) . " standout?>\n";
$ret .= "</form>\n";
return $ret;
}
###
### we have a post, process edits
###
if (LJ::did_post()) {
return "<?h1 $ML{'Error'} h1?><?p $ML{'error.invalidform'} p?>" unless LJ::check_form_auth();
$POST{'unban'} =~ s/\0/,/g;
return "<?badinput?>" unless LJ::text_in(\%POST);
my @errors = ();
# name
unless ($POST{'name'} || defined($POST{'name_absent'})) {
push @errors, $ML{'.error.noname'};
}
# state and zip
my ($zipcity, $zipstate);
if ($POST{'country'} eq "US") {
$sth = $dbr->prepare("SELECT city, state FROM zip WHERE zip=?");
$sth->execute($POST{'zip'});
($zipcity, $zipstate) = $sth->fetchrow_array;
}
# country
if ($POST{'country'} ne "US" && $POST{'zip'}) {
push @errors, $ML{'.error.locale.zip_requires_us'};
}
if ($POST{'country'} eq "US" && $POST{'stateother'}) {
push @errors, $ML{'.error.locale.country_ne_state'};
} elsif ($POST{'country'} && $POST{'country'} ne "US" && $POST{'statedrop'}) {
push @errors, $ML{'.error.locale.state_ne_country'};
}
# zip-code validation stuff
if ($POST{'country'} eq "US") {
if ($POST{'statedrop'} && $zipstate && $POST{'statedrop'} ne $zipstate) {
push @errors, $ML{'.error.locale.zip_ne_state'};
}
if ($zipcity) {
$POST{'statedrop'} = $zipstate;
$POST{'city'} = $zipcity;
}
}
if ($POST{'country'} && !defined($countries{$POST{'country'}})) {
push @errors, $ML{'.error.locale.invalid_country'};
}
# birthday
my $this_year = (localtime())[5]+1900;
if ($POST{'year'} && $POST{'year'} < 100) {
push @errors, $ML{'.error.year.notenoughdigits'};
}
if ($POST{'year'} && $POST{'year'} >= 100 && ($POST{'year'} < 1890 || $POST{'year'} > $this_year)) {
push @errors, $ML{'.error.year.outofrange'};
}
if ($POST{'month'} && ($POST{'month'} < 1 || $POST{'month'} > 12)) {
push @errors, $ML{'.error.month.outofrange'};
}
if ($POST{'day'} && ($POST{'day'} < 1 || $POST{'day'} > 31)) {
push @errors, $ML{'.error.day.outofrange'};
}
if (@errors == 0 && $POST{'day'} > LJ::days_in_month($POST{'month'}, $POST{'year'})) {
push @errors, $ML{'.error.day.notinmonth'};
}
# email
unless ($POST{'email'}) {
push @errors, $ML{'.error.email.none'};
}
if ($LJ::USER_EMAIL and $POST{'email'} =~ /\@\Q$LJ::USER_DOMAIN\E$/i) {
push @errors, BML::ml(".error.email.lj_domain", { 'user' => $u->{'user'}, 'domain' => $LJ::USER_DOMAIN, });
}
if ($POST{'email'} =~ /\s/) {
push @errors, $ML{'.error.email.no_space'};
}
unless (@errors) {
LJ::check_email($POST{'email'}, \@errors);
}
# text messaging
if ($POST{'use_txtmsg'}) {
unless ($POST{'txtmsg_provider'}) {
push @errors, $ML{'.error.tm.require_provider'};
}
unless ($POST{'txtmsg_number'}) {
push @errors, $ML{'.error.tm.require.number'};
}
}
return LJ::bad_input(@errors) if @errors;
### no errors
my $dbh = LJ::get_db_writer();
my $email_changed = ($u->{'email'} ne $POST{'email'});
if ($email_changed) {
# record old email address;
LJ::infohistory_add($u, 'email', $u->{email}, $u->{status});
}
$POST{'url'} =~ s/\s+$//; $POST{'url'} =~ s/^\s+//;
if ($POST{'url'} && $POST{'url'} !~ /^https?:\/\//) {
$POST{'url'} =~ s/^http\W*//;
$POST{'url'} = "http://$POST{'url'}";
}
my $newname = defined $POST{'name_absent'} ? $saved{'name'} : $POST{'name'};
$newname =~ s/[\n\r]//g;
$newname = LJ::text_trim($newname, LJ::BMAX_NAME, LJ::CMAX_NAME);
my $newbio = defined($POST{'bio_absent'}) ? $saved{'bio'} : $POST{'bio'};
my $has_bio = ($newbio =~ /\S/) ? "Y" : "N";
my $txtmsg_status = $POST{'use_txtmsg'} ? "on" : "off";
# setup what we're gonna update in the user table:
my %update = (
'name' => $newname,
'bdate' => sprintf("%04d-%02d-%02d", $POST{'year'}, $POST{'month'}, $POST{'day'}),
'email' => $POST{'email'},
'status' => ($email_changed && $u->{'status'} eq "A") ? "T" : $u->{'status'},
'has_bio' => $has_bio,
'allow_infoshow' => $POST{'allow_infoshow'} ? "Y" : "N",
'allow_getljnews' => $POST{'allow_getljnews'} ? "Y" : "N",
'opt_showtalklinks' => $POST{'opt_showtalklinks'} ? "Y" : "N",
'opt_gettalkemail' => $POST{'opt_gettalkemail'} ? "Y" : "N",
'opt_htmlemail' => $POST{'opt_htmlemail'} ? "Y" : "N",
'opt_mangleemail' => $POST{'opt_mangleemail'} ? "Y" : "N",
'opt_whocanreply' => $POST{'opt_whocanreply'},
'txtmsg_status' => $txtmsg_status,
);
if ($POST{'allow_contactshow'}) {
$update{'allow_contactshow'} = "Y";
$update{'allow_contactshow'} = "F" if $POST{'contactshow_sec'} eq "F";
} else {
$update{'allow_contactshow'} = "N";
}
if (defined $POST{'oldenc'}) {
$update{'oldenc'} = $POST{'oldenc'};
}
LJ::update_user($u, \%update);
### change any of the userprops ?
{
# journal / friends titles
$POST{'journaltitle'} = LJ::text_trim($POST{'journaltitle'}, 0, 80) if $POST{'journaltitle'};
$POST{'journalsubtitle'} = LJ::text_trim($POST{'journalsubtitle'}, 0, 80) if $POST{'journalsubtitle'};
$POST{'friendspagetitle'} = LJ::text_trim($POST{'friendspagetitle'}, 0, 80) if $POST{'friendspagetitle'};
# opts
$POST{'opt_showmutualfriends'} = $POST{'opt_showmutualfriends'} ? 1 : 0;
$POST{'opt_getselfemail'} = $POST{'opt_getselfemail'} ? 1 : 0;
$POST{'opt_stylemine'} = $POST{'opt_stylemine'} ? 1 : 0;
$POST{'opt_blockrobots'} = $POST{'opt_blockrobots'} ? 1 : 0;
$POST{'opt_bdaymail'} = $POST{'opt_bdaymail'} ? 1 : 0;
$POST{'opt_hidefriendofs'} = $POST{'opt_hidefriendofs'} ? 1 : 0;
$POST{'opt_nctalklinks'} = $POST{'opt_nctalklinks'} ? 1 : 0;
$POST{'opt_weblogscom'} = $POST{'opt_weblogscom'} ? 1 : 0;
if ($POST{'opt_logcommentips'} ne "N" &&
$POST{'opt_logcommentips'} ne "S" &&
$POST{'opt_logcommentips'} ne "A") { $POST{'opt_logcommentips'} = "N"; }
$POST{'opt_whoscreened'} = "N" unless $POST{'opt_whoscreened'} =~ m/^(N|R|F|A)$/;
$POST{'opt_imagelinks'} = 0 unless $POST{'opt_imagelinks_on'} &&
$POST{'opt_imagelinks'} =~ m/^(\d+)\|(\d+)$/;
# for the directory.
$POST{'sidx_bdate'} = "";
$POST{'sidx_loc'} = "";
$POST{'state'} = $POST{'statedrop'} || $POST{'stateother'};
if ($update{'allow_infoshow'} eq 'Y') {
if ($POST{'year'}) {
$POST{'sidx_bdate'} = sprintf("%04d-%02d-%02d", map { $POST{$_} }
qw(year month day));
}
if ($POST{'country'}) {
my $state;
if ($POST{'country'} eq "US") {
$state = $POST{'statedrop'};
} else {
$state = $POST{'stateother'};
}
$POST{'sidx_loc'} = sprintf("%2s-%s-%s",
$POST{'country'},
$state,
$POST{'city'});
}
}
my @uprops = (
"opt_whatemailshow",
"country", "state", "city", "zip", "icq",
"aolim", "yahoo", "msn", "url", "urlname",
"gender", "jabber", "opt_blockrobots",
"opt_logcommentips",
"opt_bdaymail", "opt_hidefriendofs",
"sidx_bdate", "sidx_loc", "mailencoding", "opt_nctalklinks",
"opt_whoscreened", "journaltitle", "journalsubtitle", "friendspagetitle",
"opt_stylemine", "opt_imagelinks", "opt_getselfemail",
"external_foaf_url", "opt_showmutualfriends",
);
# weblogs.com requires a special cap
push @uprops, 'opt_weblogscom' if LJ::get_cap($u, 'weblogscom');
# this is only done once, then never appears again.
push @uprops, 'howhear' if $POST{'howhear'};
# set userprops
foreach my $uprop (@uprops) {
my $eff_val = $POST{$uprop}; # effective value, since 0 isn't stored
$eff_val = "" unless $eff_val;
my $mem_only = $eff_val eq $u->{$uprop};
LJ::set_userprop($u, $uprop, $eff_val, $mem_only);
}
}
# update their bio text
if (($u->{'bio'} ne $POST{'bio'}) && !defined($POST{'bio_absent'})) {
if ($has_bio eq "N") {
$u->do("DELETE FROM userbio WHERE userid=?", undef, $u->{'userid'});
$u->dudata_set('B', 0, 0);
} else {
$u->do("REPLACE INTO userbio (userid, bio) VALUES (?, ?)",
undef, $u->{'userid'}, $POST{'bio'});
$u->dudata_set('B', 0, length($POST{'bio'}));
}
LJ::MemCache::set([$u->{'userid'}, "bio:$u->{'userid'}"], $POST{'bio'});
}
# update their text messaging info
if ($txtmsg_status eq "off" && $u->{'txtmsg_status'} eq "on") {
$dbh->do("DELETE FROM txtmsg WHERE userid=?", undef, $u->{'userid'});
} elsif ($txtmsg_status eq "on") {
$dbh->do("REPLACE INTO txtmsg (userid, provider, number, security) VALUES (?, ?, ?, ?)",
undef, $u->{'userid'}, $POST{'txtmsg_provider'}, $POST{'txtmsg_number'}, $POST{'txtmsg_security'});
}
# update interests
unless ($POST{'interests_absent'}) {
$POST{'interests'} =~ s/^\s+//;
$POST{'interests'} =~ s/\s+$//;
$POST{'interests'} =~ s/\n/,/g;
$POST{'interests'} =~ s/\s+/ /g; #Strip duplicate spaces from the interest
my @ints = split (/\s*,\s*/, $POST{'interests'});
my $intcount = scalar(@ints);
if ($intcount > 150) {
return LJ::bad_input(BML::ml(".error.excessive_int", {'intcount' => $intcount}));
}
LJ::set_interests($u, \%interests, \@ints);
}
# now unban users they selected to be unbanned
if ($POST{'unban'}) {
my $bannedin = join(",", map { $dbh->quote($_); } split(/,/, $POST{'unban'}));
$dbh->do("DELETE FROM reluser WHERE userid=? AND type='B' AND targetid IN ($bannedin)", undef, $u->{'userid'});
}
# actions if email changed
if ($email_changed) {
my $aa = {};
$aa = LJ::register_authaction($u->{'userid'},
"validateemail", $POST{'email'});
LJ::send_mail({
'to' => $POST{'email'},
'from' => $LJ::ADMIN_EMAIL,
'charset' => 'utf-8',
'subject' => $ML{'.newemail.subject'},
'body' => BML::ml('.newemail.body2',
{ username => $u->{user},
sitename => $LJ::SITENAME,
sitelink => $LJ::SITEROOT,
conflink => "$LJ::SITEROOT/confirm/$aa->{'aaid'}.$aa->{'authcode'}" }),
});
}
# tell the user all is well
return "<?h1 $ML{'.success.header'} h1?>\n" .
"<?p " . BML::ml(".success.message", { 'user' => $u->{'user'}, }) . " p?>";
}
# should never happen
return "<?h1 $ML{'Error'} h1?><?p $ML{'error.unknownmode'} p?>";
}
_code?>
<=body
page?><?_c <LJDEP>
lib: LJ::TextMessage, cgi-bin/ljlib.pl, cgi-bin/ljlang.pl
link: htdocs/legal/privacy.bml, htdocs/support/faqbrowse.bml, htdocs/tools/textmessage.bml, htdocs/uploadpic.bml
link: htdocs/paidaccounts/index.bml, htdocs/users, htdocs/userinfo.bml
post: htdocs/editinfo.bml
img: htdocs/userpic
</LJDEP> _c?>

View File

@@ -0,0 +1,413 @@
<?page
title=><?_ML .title _ML?>
body<=
<?_code
{
use strict;
use vars qw(%GET %POST);
my $head = \$_[1]->{'head'};
my $bodyopts = \$_[1]->{'bodyopts'};
LJ::set_active_crumb('editentries');
my $remote = LJ::get_remote();
return LJ::bad_input($ML{'error.noremote'})
unless $remote;
if ($remote->underage) {
return BML::redirect("$LJ::SITEROOT/agecheck/?s=1");
}
my $mode = $GET{'mode'} || $POST{'mode'} || "init";
if ($GET{'itemid'} || $POST{'itemid'}) { $mode = "edit"; }
my $ret;
my $getextra = "?authas=$GET{'authas'}" if $GET{'authas'};
# are they asking to be authed as someone else?
my $authas = $GET{'authas'} || $remote->{'user'};
my $u = LJ::get_authas_user($authas);
return LJ::bad_input("You could not be authenticated as the specified user.")
unless $u;
return LJ::bad_input("You must be authenticated as a person.")
unless $u->{'journaltype'} eq 'P';
if ($mode eq "edit")
{
# are we modify a community post?
my $usejournal = $GET{'usejournal'} || $POST{'usejournal'} || $GET{'journal'};
undef $usejournal if $usejournal eq $u->{'user'}; # ignore if it's the user
# user object for community if we're modifying one
my $usejournal_u;
if ($usejournal) {
$usejournal_u = LJ::load_user($usejournal);
return LJ::bad_input("The community you selected does not exist.")
unless $usejournal_u;
}
# extra get arguments
my $getextra;
$getextra .= "authas=$authas&" if $authas ne $u->{'user'};
$getextra .= "usejournal=$usejournal&" if $usejournal;
chop $getextra;
$getextra = "?$getextra" if $getextra;
###
### HAVE AN ITEMID TO EDIT
###
if ($GET{'itemid'} || $POST{'itemid'}) {
# the 'itemid' form element is really an 'itemid'
my $ditemid = $GET{'itemid'} || $POST{'itemid'};
my $anum = $ditemid % 256;
my $itemid = $ditemid >> 8;
# do getevents request
my %res = ();
LJ::do_request({ 'mode' => 'getevents',
'selecttype' => 'one',
'ver' => $LJ::PROTOCOL_VER,
'user' => $u->{'user'},
'usejournal' => $usejournal,
'itemid' => $itemid },
\%res,
{ "noauth" => 1,
'u' => $u }
);
# was there a protocol error?
return "<?h1 $ML{'Error'} h1?><?p $res{'errmsg'} p?>"
unless $res{'success'} eq 'OK';
# does the requested entry exist?
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.nofind'} p?>"
unless $res{'events_count'} && $res{'events_1_anum'} == $anum;
# are we authorized to edit other peoples' posts in this community?
my $disabled_save = 0;
my $disabled_delete = 0;
my $disabled_spamdelete = 0;
if ($usejournal && $res{'events_1_poster'} ne $u->{'user'}) {
$disabled_delete = ! LJ::can_delete_journal_item($u, $usejournal_u);
$disabled_save++;
}
$disabled_spamdelete = $disabled_delete || !$usejournal || ($res{'events_1_poster'} eq $u->{'user'});
###
### SAVE EDITS
###
# are we spellchecking before we post?
my $spellcheck_html;
my $did_spellcheck;
if ($LJ::SPELLER && $POST{'action:spellcheck'}) {
$did_spellcheck++;
my $s = new LJ::SpellCheck { 'spellcommand' => $LJ::SPELLER,
'color' => '<?hotcolor?>', };
my $event = LJ::ehtml($POST{'event'});
$spellcheck_html = $s->check_html(\$event);
$spellcheck_html = "<?inerr $ML{'entryform.spellcheck.noerrors'} inerr?>" unless $spellcheck_html ne "";
}
# they clicked the save or delete button
if (!$spellcheck_html && ($POST{'action:save'} || $POST{'action:delete'} || $POST{'action:deletespam'})) {
return LJ::bad_input($ML{'error.invalidform'}) unless LJ::check_form_auth();
my %req = ( 'mode' => 'editevent',
'ver' => $LJ::PROTOCOL_VER,
'user' => $u->{'user'},
'usejournal' => $usejournal,
'itemid' => $itemid,
);
LJ::entry_form_decode(\%req, \%POST);
# Delete
$req{'event'} = '' if $POST{'action:delete'} || $POST{'action:deletespam'};
# mark as spam, if need be
LJ::mark_entry_as_spam($usejournal_u, $itemid) if $POST{'action:deletespam'};
# if the action is to delete it, then let's note that
if ($POST{'action:delete'} || $POST{'action:deletespam'}) {
# now log the event created above
($usejournal ? $usejournal_u : $u)->log_event('delete_entry', {
remote => $remote,
actiontarget => $ditemid,
method => 'web',
});
}
# do editevent request
LJ::do_request(\%req, \%res, { 'noauth' => 1, 'u' => $u });
# check response
unless ($res{'success'} eq "OK") {
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.modify'} <ul><li><b>$res{'errmsg'}</b></li></ul> p?>";
}
# deleted
unless ($req{'event'}) {
my $result = "<?h1 $ML{'.success.head'} h1?><?p $ML{'.success.delete'} p?>";
$result .= "<?p $ML{'.success.deletespam'} p?>" if $POST{'action:deletespam'};
return $result;
}
# modified
return BML::redirect(LJ::item_link($usejournal ? $usejournal_u : $u, $itemid, $res{'anum'}));
}
###
### SHOW EDIT FORM
###
my $auth = "<tr><th>$ML{'.auth.poster'}</th><td>";
$auth .= $usejournal ? LJ::ljuser($res{'events_1_poster'}) . " in community " . LJ::ljuser($usejournal) :
LJ::ljuser($remote);
$auth .= "</td></tr>";
my ($year, $mon, $mday, $hour, $min) = split(/\D/, $res{"events_1_eventtime"});
my $datetime; my $date = LJ::html_datetime_decode({ 'name' => "date_ymd", }, \%POST);
if ($date ne "0000-00-00 00:00:00") {
my ($date, $time) = split( / /, $date);
$datetime = "$date $POST{'hour'}:$POST{'min'}";
} else {
$datetime = "$year-$mon-$mday $hour:$min";
}
my $subject = $POST{'subject'} || $res{'events_1_subject'};
my $event = $POST{'event'} || $res{'events_1_event'};
my $curmask = $res{'events_1_allowmask'};
my $cursec = $res{'events_1_security'} || $POST{'security'};
if ($cursec eq 'usemask') {
$cursec = $curmask == 1 ? "friends" : "custom";
}
# start edit form
my $ret; my $js;
$ret .= "<div style='width: 100%'><form method='post' action='editjournal.bml$getextra' id='updateForm' name='updateForm'>";
$ret .= LJ::form_auth();
$ret .= LJ::html_hidden('itemid', $ditemid,'mode','edit','edited',1) . "\n";
my $entry = {
'mode' => "edit",
'auth_as_remote' => 1,
'subject' => $subject,
'event' => $event,
'datetime' => $datetime,
'usejournal' => $usejournal,
'security' => $cursec,
'security_mask' => $curmask,
'auth' => $auth,
'remote' => $remote,
'spellcheck_html' => $spellcheck_html,
'richtext' => 0,
'mood' => $res{'events_1_'},
'disabled_save' => $disabled_save,
'disabled_delete' => $disabled_delete,
'disabled_spamdelete' => $disabled_spamdelete,
};
for (my $i = 1; $i <= $res{'prop_count'}; $i++) {
$entry->{"prop_" . $res{"prop_${i}_name"}} = $res{"prop_${i}_value"};
}
foreach ( keys %POST ) { $entry->{$_} = %POST->{$_}; }
my $onload = "shift_contents(); ";
$ret .= LJ::entry_form($entry, \$$head, \$onload);
$ret .= "</form></div>";
$$bodyopts .= "onload='$onload' onresize='shift_contents();' ";
return $ret;
}
###
### NO ITEMID - SELECT ENTRY TO EDIT
###
### already authenticated from above
return BML::redirect("$LJ::SITEROOT/editjournal.bml")
unless LJ::did_post();
my %res;
my %req = (
'mode' => 'getevents',
'ver' => $LJ::PROTOCOL_VER,
'user' => $u->{'user'},
'usejournal' => $usejournal,
'truncate' => 300,
'noprops' => 1,
);
# last 1
if ($POST{'selecttype'} eq "last") {
$req{'selecttype'} = 'one';
$req{'itemid'} = -1;
# last n
} elsif ($POST{'selecttype'} eq 'lastn') {
$req{'selecttype'} = 'lastn';
$req{'howmany'} = $POST{'howmany'};
# day
} elsif ($POST{'selecttype'} eq 'day') {
$req{'selecttype'} = 'day';
$req{$_} = $POST{$_} foreach qw(year month day);
}
# do getevents request
LJ::do_request(\%req, \%res, { 'noauth' => 1, 'u' => $u });
# check response
unless ($res{'success'} eq "OK") {
return "<?h1 $ML{'Error'} h1?>\n" .
"<?p $ML{'.error.getting'} <ul><li><b><?_eh $res{'errmsg'} _eh?></b></li></ul> p?>";
}
# only one item returned? go directly to edit it
if ($res{'events_count'} == 1) {
my $ditemid = ($res{'events_1_itemid'} << 8) + $res{'events_1_anum'};
my $ditemid_get = $getextra ? "$getextra&itemid=$ditemid" : "?itemid=$ditemid";
return BML::redirect("$LJ::SITEROOT/editjournal.bml$ditemid_get");
}
# how many results did we get?
my $ev_count = $res{'events_count'};
unless ($ev_count) {
if ($req{'selecttype'} eq 'lastn') {
return "<?h1 No Entries Found h1?>\n" .
"<?p The selected journal has no entries. p?>\n";
}
return "<?h1 No Entries Found h1?>\n" .
"<?p No entries match the criteria you specified. Please go back and adjust your search. p?>\n";
}
### display results
my $ret;
$ret .= "<?h1 $ML{'.pickentry.head'} h1?><?p $ML{'.pickentry.text'} p?>\n";
my %props = ();
for (my $i=1; $i<=$res{'prop_count'}; $i++) {
$props{$res{"prop_${i}_itemid"}}->{$res{"prop_${i}_name"}} = $res{"prop_${i}_value"};
}
for (my $i=1; $i<=$ev_count; $i++) {
my $itemid = $res{"events_${i}_itemid"};
my $ditemid = $itemid * 256 + $res{"events_${i}_anum"};
$ret .= "<?hr?><table><tr valign='top'><td align='middle'>";
$ret .= "<form method='post' action='editjournal.bml$getextra'>\n";
$ret .= LJ::html_hidden('itemid',$ditemid,'mode',"edit");
$ret .= LJ::html_submit('itemid-$ditemid','Edit this Entry');
$ret .= "</form>";
$ret .= "</td><td>";
$ret .= " <b><label for='itemid-$ditemid'>" . $res{"events_${i}_eventtime"} . "</label></b>";
$ret .= " (Posted by: " . LJ::ljuser($res{"events_${i}_poster"}) . ")" if $usejournal;
### security indicator
my $sec = ' ';
if ($res{"events_${i}_security"} eq "private") {
$sec .= BML::fill_template("securityprivate");
} elsif ($res{"events_${i}_security"} eq "usemask") {
$sec .= BML::fill_template("securityprotected");
}
$ret .= $sec;
if (my $subj = $res{"events_${i}_subject"}) {
LJ::CleanHTML::clean_subject_all(\$subj);
$ret .= " <i>" . LJ::ehtml($subj) . "</i>";
}
$ret .= "<br />\n";
my $event = LJ::ehtml(LJ::durl($res{"events_${i}_event"}));
$event =~ s!\n!<br />!g;
$ret .= $event;
$ret .= "</td></tr></table>\n";
}
return $ret;
} elsif ($mode eq "init") {
# no authentication needs to be done on this page, it's just a form anyway
# user switcher
$ret .= "<form action='editjournal.bml' method='get'>\n";
$ret .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'}, 'type' => 'P' });
$ret .= "</form>\n\n";
# header
$ret .= "<?p $ML{'.desc'} p?>\n";
# edit form
$ret .= "<form action='editjournal.bml$getextra' method='post'>\n";
$ret .= LJ::html_hidden("mode","edit");
$ret .= "<?standout <table>\n";
# view type
$ret .= "<tr valign=\"top\"><td>$ML{'.viewwhat'}</td>\n<td>\n";
$ret .= LJ::html_check({ 'type' => 'radio', 'name' => 'selecttype', 'id' => 'selecttype-last',
'value' => 'last', 'selected' => 1 });
$ret .= "<label for='selecttype-last'>$ML{'.recententry'}</label><br />\n";
$ret .= LJ::html_check({ 'type' => 'radio', 'name' => 'selecttype',
'id' => 'selecttype-lastn', 'value' => 'lastn' }) . " ";
$ret .= LJ::html_text({ 'name' => 'howmany', 'size' => '3', 'maxlength' => '2', 'value' => '20',
'onchange' => "checkRadioButton('selecttype-lastn');" }) . " ";
$ret .= "<label for='selecttype-lastn'>$ML{'.recententries'}</label><br />\n";
$ret .= LJ::html_check({ 'type' => 'radio', 'name' => 'selecttype',
'id' => 'selecttype-day', 'value' => 'day' });
$ret .= "<label for='selecttype-day'>$ML{'.certainday'}</label>";
my @time = localtime(time);
my $mday = sprintf("%02d", $time[3]);
my $mon = sprintf("%02d", $time[4] + 1);
my $year = $time[5] + 1900;
$ret .= LJ::html_text({ 'name' => 'year', 'size' => '5', 'maxlength' => '4', 'value' => $year,
'onchange' => "checkRadioButton('selecttype-day');" }) . "-";
$ret .= LJ::html_text({ 'name' => 'month', 'size' => '3', 'maxlength' => '2', 'value' => $mon,
'onchange' => "checkRadioButton('selecttype-day');" }) . "-";
$ret .= LJ::html_text({ 'name' => 'day', 'size' => '3', 'maxlength' => '2', 'value' => $mday,
'onchange' => "checkRadioButton('selecttype-day');" }) . "\n";
$ret .= "</td></tr>\n";
# use journal
$ret .= "<tr valign=\"top\"><td align='right'>$ML{'.in'}</td>\n<td>\n";
$ret .= LJ::html_text({ 'name' => 'usejournal', 'size' => '20', 'maxlength' => '15', 'value' => $GET{'usejournal'} }) . " ";
$ret .= " (optional)</td></tr>";
# submit button
$ret .= "<tr><td>&nbsp;</td><td>" . LJ::html_submit(undef, $ML{'.btn.proceed'}) . "</td></tr>\n";
$ret .= "</table> standout?>\n";
$ret .= "</form>\n";
return $ret;
}
}
_code?>
<=body
bodyopts=><?_code return $_[1]->{'bodyopts'}; _code?>
head<=
<?entryformcss?>
<?_code return $_[1]->{'head'}; _code?>
<script type="text/javascript" language="JavaScript" src="/js/entry.js"></script>
<?_code return $LJ::COMMON_CODE{'autoradio_check'}; _code?>
<=head
page?><?_c <LJDEP>
post: htdocs/editjournal.bml
link: htdocs/lostinfo.bml
</LJDEP> _c?>

873
livejournal/htdocs/editpics.bml Executable file
View File

@@ -0,0 +1,873 @@
<?_code
{
use strict;
use vars qw(%GET %POST $title $body @errors);
LJ::set_active_crumb('editpics');
$body = "";
@errors = ();
my $err = sub {
$title = "Error";
$body = LJ::bad_input(@_);
return;
};
unless (LJ::text_in(\%POST)) {
return $err->("Invalid UTF-8 Input");
}
my $remote = LJ::get_remote();
return $err->($ML{'error.noremote'})
unless $remote;
if ($remote->underage) {
return BML::redirect("$LJ::SITEROOT/agecheck/?s=1");
}
my $authas = $GET{'authas'} || $remote->{'user'};
my $u = LJ::get_authas_user($authas);
return $err->($ML{'error.invalidauth'})
unless $u;
# extra arguments for get requests
my $getextra = $authas ne $remote->{'user'} ? "?authas=$authas" : '';
my $returl = LJ::CleanHTML::canonical_url($POST{'ret'});
my $picurl = LJ::CleanHTML::canonical_url($POST{'urlpic'});
my $fotobilder = index($returl, $LJ::FB_SITEROOT) == 0 &&
$picurl =~ m!^$LJ::FB_SITEROOT/~?$remote->{'user'}/pic/!;
if ($fotobilder &&
(LJ::check_referer($returl) || LJ::check_referer('/editpics.bml'))) {
return $err->('Invalid referring site or redirection not allowed')
unless $returl =~ /$LJ::FB_DOMAIN/ && LJ::get_cap($u, 'fb_account');
}
if (LJ::get_cap($u, "readonly")) {
$title = "Read-only mode";
$body = $LJ::MSG_READONLY_USER;
return;
}
# update this user's activated pics
LJ::activate_userpics($u);
my ($dbh, $dbcm, $dbcr, $sth);
# Put $count and $max in larger scope so they can be used in output
my $count;
$dbcm = LJ::get_cluster_master($u);
return $err->($ML{'error.nodb'}) unless $dbcm;
if ($u->{'dversion'} > 6) {
$dbcr = LJ::get_cluster_def_reader($u);
return $err->($ML{'error.nodb'}) unless $dbcr;
$count = $dbcr->selectrow_array("SELECT COUNT(*) FROM userpic2 " .
"WHERE userid=? AND state <> 'X'", undef, $u->{'userid'});
} else {
$dbh = LJ::get_db_writer();
return $err->($ML{'error.nodb'}) unless $dbh;
$count = $dbh->selectrow_array("SELECT COUNT(*) FROM userpic " .
"WHERE userid=? AND state <> 'X'", undef, $u->{'userid'});
}
my $max = LJ::get_cap($u, "userpics");
### save mode
if (LJ::did_post()) {
### save changes to existing pics
if ($POST{'action:save'}) {
# form being posted isn't multipart, since we were able to read from %POST
my %exist_kwids;
if ($u->{'dversion'} > 6) {
$sth = $dbcr->prepare("SELECT kwid, picid FROM userpicmap2 WHERE userid=?");
} else {
$sth = $dbh->prepare("SELECT kwid, picid FROM userpicmap WHERE userid=?");
}
$sth->execute($u->{'userid'});
while (my ($kwid, $picid) = $sth->fetchrow_array) {
push @{$exist_kwids{$picid}}, $kwid;
}
my @inactive_picids;
my @delete;
my %picid_of_kwid;
my %ctype; # picid -> contenttype, for delete mode
my %states; # picid -> state, for setting new default
my %locations; # picid -> location, for deleting
my @comments;
my %exist_comments;
# select all of their userpics and iterate through them
if ($u->{'dversion'} > 6) {
$sth = $dbcr->prepare("SELECT picid, width, height, state, fmt, comment, location " .
"FROM userpic2 WHERE userid=?");
} else {
$sth = $dbh->prepare("SELECT picid, width, height, state, contenttype " .
"FROM userpic WHERE userid=?");
}
$sth->execute($u->{'userid'});
while (my $pic = $sth->fetchrow_hashref)
{
# ignore anything expunged
next if $pic->{state} eq 'X';
# store picture information
$states{$pic->{picid}} = $pic->{state};
$locations{$pic->{picid}} = $pic->{location}
if $u->{dversion} > 6;
$exist_comments{$pic->{picid}} = $pic->{comment};
# delete this pic
if ($POST{"delete_$pic->{'picid'}"}) {
push @delete, $pic->{'picid'};
$ctype{$pic->{picid}} = ($u->{'dversion'} > 6) ? $pic->{'fmt'} : $pic->{'contenttype'};
next;
}
# make a list of inactive picids
if ($pic->{'state'} eq 'I') {
push @inactive_picids, $pic->{'picid'};
next;
}
# we're going to modify keywords on active pictures
my $c = 1;
my @kw_errors;
my @keywords = split(/\s*,\s*/, $POST{"kw_$pic->{'picid'}"});
@keywords = grep { s/^\s+//; s/\s+$//; $_; } @keywords;
foreach my $kw (@keywords) {
my $kwid = ($u->{'dversion'} > 6) ? LJ::get_keyword_id($u, $kw) : LJ::get_keyword_id($kw);
next unless $kwid;
if ($c > $LJ::MAX_USERPIC_KEYWORDS) {
my $ekw = LJ::ehtml($kw);
push @kw_errors, $ekw;
next;
}
if ($picid_of_kwid{$kwid}) {
my $ekw = LJ::ehtml($kw);
push @errors, BML::ml(".error.keywords", {'ekw' => $ekw});
}
$picid_of_kwid{$kwid} = $pic->{'picid'};
$c++;
}
# Let the user know about any we didn't save
if (@kw_errors) {
my $num_words = scalar(@kw_errors);
my $kws = join (", ", @kw_errors);
push @errors, BML::ml(".error.toomanykeywords", {'numwords' => $num_words, 'words' => $kws, 'max' => $LJ::MAX_USERPIC_KEYWORDS});
}
# Find if they changed the comment and then save the new one
if ($u->{'dversion'} > 6 && $POST{"com_$pic->{'picid'}"} ne $exist_comments{$pic->{'picid'}}) {
my $comment = LJ::text_trim($POST{"com_$pic->{'picid'}"}, LJ::BMAX_UPIC_COMMENT, LJ::CMAX_UPIC_COMMENT);
$u->do("UPDATE userpic2 SET comment=? WHERE userid=? AND picid=?",
undef, $comment, $u->{'userid'}, $pic->{'picid'});
}
}
# now, reapply the existing picids to the inactive pics, unless
# that picid has already been assigned to a new active one
foreach my $picid (@inactive_picids) {
next unless $exist_kwids{$picid};
foreach (@{$exist_kwids{$picid}}) {
$picid_of_kwid{$_} ||= $picid;
}
}
if (@delete) {
my $id_in;
if ($u->{'dversion'} > 6) {
$id_in = join(", ", map { $dbcm->quote($_) } @delete);
} else {
$id_in = join(", ", map { $dbh->quote($_) } @delete);
}
# delete data from user cluster
foreach my $picid (@delete) {
my $fmt;
if ($u->{'dversion'} > 6) {
$fmt = {
'G' => 'gif',
'J' => 'jpg',
'P' => 'png',
}->{$ctype{$picid}};
} else {
$fmt = {
'image/gif' => 'gif',
'image/jpeg' => 'jpg',
'image/png' => 'png',
}->{$ctype{$picid}};
}
my $deleted = 0;
# try and delete from either the blob server or database,
# and only after deleting the image do we delete the metadata.
if ($locations{$picid} eq 'mogile') {
$deleted = 1
if LJ::mogclient()->delete($u->mogfs_userpic_key($picid));
} elsif ($LJ::USERPIC_BLOBSERVER &&
LJ::Blob::delete($u, "userpic", $fmt, $picid)) {
$deleted = 1;
} elsif ($u->do("DELETE FROM userpicblob2 WHERE ".
"userid=? AND picid=?", undef,
$u->{userid}, $picid) > 0) {
$deleted = 1;
}
# now delete the metadata if we got the real data
if ($deleted) {
if ($u->{'dversion'} > 6) {
$u->do("DELETE FROM userpic2 WHERE picid=? AND userid=?",
undef, $picid, $u->{'userid'});
} else {
$dbh->do("DELETE FROM userpic WHERE picid=?", undef, $picid);
}
$u->do("DELETE FROM userblob WHERE journalid=? AND blobid=? " .
"AND domain=?", undef, $u->{'userid'}, $picid,
LJ::get_blob_domainid('userpic'));
# decrement $count to reflect deletion
$count--;
}
# if we didn't end up deleting, it's either because of
# some transient error, or maybe there was nothing to delete
# for some bizarre reason, in which case we should verify
# that and make sure they can delete their metadata
if (! $deleted) {
my $present;
if ($locations{$picid} eq 'mogile') {
my $blob = LJ::mogclient()->get_file_data($u->mogfs_userpic_key($picid));
$present = length($blob) ? 1 : 0;
} elsif ($LJ::USERPIC_BLOBSERVER) {
my $blob = LJ::Blob::get($u, "userpic", $fmt, $picid);
$present = length($blob) ? 1 : 0;
}
$present ||= $dbcm->selectrow_array("SELECT COUNT(*) FROM userpicblob2 WHERE ".
"userid=? AND picid=?", undef, $u->{'userid'},
$picid);
if (! int($present)) {
if ($u->{'dversion'} > 6) {
$u->do("DELETE FROM userpic2 WHERE picid=? AND userid=?",
undef, $picid, $u->{'userid'});
} else {
$dbh->do("DELETE FROM userpic WHERE picid=?", undef, $picid);
}
}
}
}
# if any of the userpics they want to delete are active, then we want to
# re-run LJ::activate_userpics() - turns out it's faster to not check to
# see if we need to do this
LJ::activate_userpics($u);
}
if (%picid_of_kwid) {
if ($u->{'dversion'} > 6) {
$u->do("REPLACE INTO userpicmap2 (userid, kwid, picid) VALUES " .
join(",", map { "(" .
join(",",
$dbcm->quote($u->{'userid'}),
$dbcm->quote($_),
$dbcm->quote($picid_of_kwid{$_})) .
")"
}
keys %picid_of_kwid)
);
} else {
$dbh->do("REPLACE INTO userpicmap (userid, kwid, picid) VALUES " .
join(",", map { "(" .
join(",",
$dbh->quote($u->{'userid'}),
$dbh->quote($_),
$dbh->quote($picid_of_kwid{$_})) .
")"
}
keys %picid_of_kwid)
);
}
}
# Delete keywords that are no longer being used
my @kwid_del;
foreach my $kwids (values %exist_kwids) {
foreach my $kwid (@$kwids) {
if (! $picid_of_kwid{$kwid}) {
push @kwid_del, $kwid+0;
}
}
}
if (@kwid_del) {
my $kwid_del = join(",", @kwid_del);
if ($u->{'dversion'} > 6) {
$u->do("DELETE FROM userpicmap2 WHERE userid=$u->{userid} " .
"AND kwid IN ($kwid_del)");
} else {
$dbh->do("DELETE FROM userpicmap WHERE userid=$u->{userid} " .
"AND kwid IN ($kwid_del)");
}
}
my $new_default = $POST{'defaultpic'}+0;
if ($POST{"delete_$POST{'defaultpic'}"}) {
# deleting your default
$new_default = 0;
}
if ($new_default != $u->{'defaultpicid'}) {
# see if they are trying to make an inactive userpic their default
if ($states{$new_default} eq 'N' || !$new_default) {
LJ::update_user($u, { defaultpicid => $new_default });
$u->{'defaultpicid'} = $new_default;
}
}
my $memkey = [$u->{'userid'},"upicinf:$u->{'userid'}"];
LJ::MemCache::delete($memkey);
$memkey = [$u->{'userid'},"upiccom:$u->{'userid'}"];
LJ::MemCache::delete($memkey);
$memkey = [$u->{'userid'},"upicurl:$u->{'userid'}"];
LJ::MemCache::delete($memkey);
}
### no post, so we'll parse the multipart data
unless (%POST) {
my $MAX_UPLOAD = 40960;
my $error;
# Add some slop to account for the size of the form headers etc.
BML::parse_multipart(\%POST, \$error, $MAX_UPLOAD + 2048);
# was there an error parsing the multipart form?
if ($error) {
if ($error =~ /^\[(\S+?)\]/) {
my $code = $1;
if ($code eq "toolarge") {
return $err->(BML::ml('.error.filetoolarge',
{ 'maxsize' => int($MAX_UPLOAD / 1024) .
$ML{'.kilobytes'} }));
}
$error = BML::ml("BML.parse_multipart.$code");
}
return $err->($error) if $error;
}
# error check input contents
if ($POST{'src'} eq "url" && $POST{'urlpic'} !~ /^http:\/\//) {
return $err->($ML{'.error.badurl'});
}
if ($POST{'src'} eq "file") {
# already loaded from multipart parse earlier
} elsif ($POST{'src'} eq "url") {
require LWPx::ParanoidAgent;
my $ua = LWPx::ParanoidAgent->new(
timeout => 10,
max_size => $MAX_UPLOAD + 1024,
);
my $res = $ua->get($POST{urlpic});
$POST{userpic} = $res->content if $res && $res->is_success;
return $err->($ML{'.error.urlerror'}) unless $POST{userpic};
}
if (length($POST{'userpic'}) > $MAX_UPLOAD) {
return $err->(BML::ml('.error.filetoolarge',
{ 'maxsize' => int($MAX_UPLOAD / 1024) .
$ML{'.kilobytes'} }));
}
my ($sx, $sy, $filetype) = Image::Size::imgsize(\$POST{'userpic'});
unless (defined $sx) {
return $err->($ML{'.error.invalidimage'});
}
unless ($filetype eq "GIF" || $filetype eq "JPG" || $filetype eq "PNG") {
return $err->(BML::ml(".error.unsupportedtype",
{ 'filetype' => $filetype }));
}
if ($sx > 100 || $sy > 100) {
return $err->( BML::ml(".error.imagetoolarge",
{ 'imagesize' => "${sx}$ML{'.imagesize.by'}${sy}",
'maxsize' => "100$ML{'.imagesize.by'}100" }) );
}
my $base64 = Digest::MD5::md5_base64($POST{'userpic'});
## see if they have too many pictures uploaded
if ($count >= $max) {
return $err->( BML::ml(".error.toomanypics2",
{ 'maxpics' => $max }) .
LJ::help_icon('userpics', " ", ""));
}
# see if it's a duplicate
my $picid;
my $contenttype;
if ($u->{'dversion'} > 6) {
if ($filetype eq "GIF") { $contenttype = 'G'; }
elsif ($filetype eq "PNG") { $contenttype = 'P'; }
elsif ($filetype eq "JPG") { $contenttype = 'J'; }
$picid = $dbcr->selectrow_array("SELECT picid FROM userpic2 " .
"WHERE userid=? AND fmt=? " .
"AND md5base64=?",
undef, $u->{'userid'}, $contenttype, $base64);
} else {
if ($filetype eq "GIF") { $contenttype = "image/gif"; }
elsif ($filetype eq "PNG") { $contenttype = "image/png"; }
elsif ($filetype eq "JPG") { $contenttype = "image/jpeg"; }
$picid = $dbh->selectrow_array("SELECT picid FROM userpic " .
"WHERE userid=? AND contenttype=? " .
"AND md5base64=?",
undef, $u->{'userid'}, $contenttype, $base64);
}
# if picture isn't a duplicate, insert it
if ($picid == 0) {
# insert the meta-data
# Make a new global picid
$picid = LJ::alloc_global_counter('P') or
return $err->('Unable to allocate new picture id');
# see where we're inserting this
my $target;
if ($u->{dversion} > 6 && $LJ::USERPIC_MOGILEFS) {
$target = 'mogile';
} elsif ($LJ::USERPIC_BLOBSERVER) {
$target = 'blob';
}
my $dberr = 0;
if ($u->{'dversion'} > 6) {
$u->do("INSERT INTO userpic2 (picid, userid, fmt, width, height, " .
"picdate, md5base64, location) VALUES (?, ?, ?, ?, ?, NOW(), ?, ?)",
undef, $picid, $u->{'userid'}, $contenttype, $sx, $sy, $base64, $target);
if ($u->err) {
push @errors, $err->($u->errstr);
$dberr = 1;
}
} else {
$dbh->do("INSERT INTO userpic (picid, userid, contenttype, width, height, " .
"picdate, md5base64) VALUES (?, ?, ?, ?, ?, NOW(), ?)",
undef, $picid, $u->{'userid'}, $contenttype, $sx, $sy, $base64);
if ($dbh->err) {
push @errors, $err->($dbh->errstr);
$dberr = 1;
}
}
my $clean_err = sub {
if ($u->{'dversion'} > 6) {
$u->do("DELETE FROM userpic2 WHERE userid=? AND picid=?",
undef, $u->{'userid'}, $picid) if $picid;
} else {
$dbh->do("DELETE FROM userpic WHERE picid=?", undef, $picid) if $picid;
}
return $err->(@_);
};
### insert the blob
if ($target eq 'mogile' && !$dberr) {
my $fh = LJ::mogclient()->new_file($u->mogfs_userpic_key($picid), 'userpics');
if (defined $fh) {
$fh->print($POST{'userpic'});
my $rv = $fh->close;
push @errors, $clean_err->("Error saving to storage server: $@") unless $rv;
} else {
# fatal error, we couldn't get a filehandle to use
push @errors, $clean_err->("Unable to contact storage server. Your picture has not been saved.");
}
} elsif ($target eq 'blob' && !$dberr) {
my $et;
my $fmt = lc($filetype);
my $rv = LJ::Blob::put($u, "userpic", $fmt, $picid, $POST{'userpic'}, \$et);
push @errors, $clean_err->("Error saving to media server: $et") unless $rv;
} elsif (!$dberr) {
my $dbcm = LJ::get_cluster_master($u);
return $err->($ML{'error.nodb'}) unless $dbcm;
$u->do("INSERT INTO userpicblob2 (userid, picid, imagedata) " .
"VALUES (?, ?, ?)",
undef, $u->{'userid'}, $picid, $POST{'userpic'});
push @errors, $clean_err->($u->errstr) if $u->err;
} else { # We should never get here!
push @errors, "User picture uploading failed for unknown reason";
}
# Not a duplicate, so increment $count
$count++;
}
# make it their default pic?
if ($POST{'make_default'}) {
LJ::update_user($u, { defaultpicid => $picid });
$u->{'defaultpicid'} = $picid;
}
# set default keywords?
if ($POST{'keywords'} ne '') {
if ($u->{'dversion'} > 6) {
$sth = $dbcr->prepare("SELECT kwid, picid FROM userpicmap2 WHERE userid=?");
} else {
$sth = $dbh->prepare("SELECT kwid, picid FROM userpicmap WHERE userid=?");
}
$sth->execute($u->{'userid'});
my @exist_kwids;
while (my ($kwid, $picid) = $sth->fetchrow_array) {
$exist_kwids[$kwid] = $picid;
}
my @keywords = split(/\s*,\s*/, $POST{'keywords'});
@keywords = grep { s/^\s+//; s/\s+$//; $_; } @keywords;
my (@bind, @data, @kw_errors);
my $c = 0;
foreach my $kw (@keywords) {
my $kwid = ($u->{'dversion'} > 6) ? LJ::get_keyword_id($u, $kw) : LJ::get_keyword_id($kw);
next unless $kwid; # Houston we have a problem! This should always return an id.
if ($c > $LJ::MAX_USERPIC_KEYWORDS) {
my $ekw = LJ::ehtml($kw);
push @kw_errors, $ekw;
next;
}
if ($exist_kwids[$kwid]) { # Already used on another picture
my $ekw = LJ::ehtml($kw);
push @errors, BML::ml(".error.keywords", {'ekw' => $ekw});
next;
} else { # New keyword, so save it
push @bind, '(?, ?, ?)';
push @data, $u->{'userid'}, $kwid, $picid;
}
$c++;
}
# Let the user know about any we didn't save
if (@kw_errors) {
my $num_words = scalar(@kw_errors);
my $kws = join (", ", @kw_errors);
push @errors, BML::ml(".error.toomanykeywords", {'numwords' => $num_words, 'words' => $kws, 'max' => $LJ::MAX_USERPIC_KEYWORDS});
}
if (@data && @bind) {
my $bind = join(',', @bind);
if ($u->{'dversion'} > 6) {
$u->do("INSERT INTO userpicmap2 (userid, kwid, picid) VALUES $bind",
undef, @data);
} else {
$dbh->do("INSERT INTO userpicmap (userid, kwid, picid) VALUES $bind",
undef, @data);
}
}
}
# set default comments and the url
if ($u->{'dversion'} > 6) {
my (@data, @set);
if ($POST{'comments'} ne '') {
push @set, 'comment=?';
push @data, LJ::text_trim($POST{'comments'}, LJ::BMAX_UPIC_COMMENT, LJ::CMAX_UPIC_COMMENT);
}
if ($POST{'url'} ne '') {
push @set, 'url=?';
push @data, $POST{'url'};
}
if (@set) {
my $set = join(',', @set);
$u->do("UPDATE userpic2 SET $set WHERE userid=? AND picid=?",
undef, @data, $u->{'userid'}, $picid);
}
}
my $memkey = [$u->{'userid'},"upicinf:$u->{'userid'}"];
LJ::MemCache::delete($memkey);
$returl = LJ::CleanHTML::canonical_url($POST{'ret'});
if ($returl) {
my $redir_host = $1 if $returl =~ m#^http://([\.:\w-]+)#i;
return BML::redirect($returl) if $LJ::REDIRECT_ALLOWED{$redir_host};
}
}
# now fall through to edit page and show the updated userpic info
}
if ($fotobilder && $POST{'md5sum'}) {
my $id;
if ($u->{'dversion'} > 6) {
$id = $dbcm->selectrow_array("SELECT picid FROM userpic2 WHERE userid=? " .
"AND md5base64=?", undef, $u->{'userid'}, $POST{'md5sum'});
} else {
$id = $dbh->selectrow_array("SELECT picid FROM userpic WHERE userid=? " .
"AND md5base64=?", undef, $u->{'userid'}, $POST{'md5sum'});
}
$fotobilder = 0 if $id;
}
# show the form to let people edit
$title = "Edit User Pictures";
# Fixme: Make this work with Fotobilder
if (!$fotobilder) {
# authas switcher form
$body .= "<form method='get' action='editpics.bml'>\n";
$body .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'} }) . "\n";
$body .= "</form>\n\n";
}
if (@errors) {
$body .= LJ::error_list(@errors);
}
if (!$fotobilder) {
my %keywords = ();
my $dbcr = LJ::get_cluster_def_reader($u);
if ($u->{'dversion'} > 6) {
$sth = $dbcr->prepare("SELECT m.picid, k.keyword FROM userpicmap2 m, userkeywords k ".
"WHERE m.userid=? AND m.kwid=k.kwid AND m.userid=k.userid");
} else {
$sth = $dbh->prepare("SELECT m.picid, k.keyword FROM userpicmap m, keywords k ".
"WHERE m.userid=? AND m.kwid=k.kwid");
}
$sth->execute($u->{'userid'});
while (my ($pic, $keyword) = $sth->fetchrow_array) {
LJ::text_out(\$keyword);
push @{$keywords{$pic}}, $keyword;
}
if ($u->{'dversion'} > 6) {
$sth = $dbcr->prepare("SELECT picid, width, height, state, comment " .
"FROM userpic2 WHERE userid=?");
} else {
$sth = $dbh->prepare("SELECT picid, width, height, state " .
"FROM userpic WHERE userid=?");
}
$sth->execute($u->{'userid'});
my @sortedpics;
push @sortedpics, $_ while $_ = $sth->fetchrow_hashref;
# See if they have any without keywords before we output the display table
foreach (@sortedpics) {
unless ($keywords{$_->{'picid'}}) {
my @w;
if (defined $LJ::HELPURL{'upic_keywords'}) {
push @w, BML::ml('.warning.keywords.faq', {'aopts' => "href='$LJ::HELPURL{'upic_keywords'}'"});
} else {
push @w, $ML{'.warning.keywords'};
}
$body .= LJ::warning_list(@w);
last;
}
}
my $piccount = 0;
foreach my $pic (sort { $a->{picid} <=> $b->{picid} } @sortedpics)
{
my $pid = $pic->{'picid'};
if ($piccount++ == 0) {
$body .= "<?h1 $ML{'.curpics'} h1?><?p $ML{'.curpics.desc'} p?>";
$body .= "<?p <strong>". BML::ml('.piclimitstatus', {current => $count, max => $max}) . "</strong> p?>";
$body .= "<form method='post' action='editpics.bml$getextra'>";
$body .= "<table cellpadding='5' border='0' cellspacing='1' " .
"style='margin-left: 30px'>";
}
$body .= "<tr valign='middle'>";
$body .= "<td align='center'><img src='$LJ::USERPIC_ROOT/$pid/$u->{'userid'}' width='$pic->{'width'}' height='$pic->{'height'}'></td>";
$body .= "<td>\n<table>";
my ($dis, $distxt);
{
$body .= "<tr><td align='right'><b><label for='$pid-def'>$ML{'.label.default'}</label></b></td><td> ";
$body .= LJ::html_check({ 'type' => 'radio', 'name' => 'defaultpic', 'value' => $pid,
'selected' => $u->{'defaultpicid'} == $pid,
'disabled' => $pic->{'state'} eq 'I' });
$body .= "&nbsp;&nbsp;&nbsp;<b><label for='$pid-del'>$ML{'.label.delete'}</label></b> ";
$body .= LJ::html_check({ 'type' => 'checkbox', 'name' => "delete_$pid",
'id' => "$pid-del", 'value' => 1 });
if ($pic->{'state'} eq 'I') {
$body .= " &nbsp;<i>[$ML{'userpic.inactive'}]</i> " . LJ::help_icon('userpic_inactive');
}
$body .= "</td></tr>\n";
}
{
my $keywords;
$keywords = join(", ", sort { lc($a) cmp lc($b) } @{$keywords{$pic->{'picid'}}})
if $keywords{$pid};
$body .= "<tr><td align='right'><b><label for='$pid-key'>$ML{'.label.keywords'}</label></b></td><td> ";
$body .= LJ::html_text({'name' => "kw_$pid", 'id' => "$pid-key",
'size' => '30', 'value' => $keywords,
'disabled' => $pic->{'state'} eq 'I' });
$body .= "</td></tr>\n";
if ($u->{'dversion'} > 6) {
$body .= "<tr><td align='right'><b><label for='$pid-com'>$ML{'.label.comment'}</label></b></td><td> ";
$body .= LJ::html_text({ 'name' => "com_$pid", 'id' => "$pid-com",
'size' => '30', 'value' => $pic->{'comment'},
'maxlength' => LJ::CMAX_UPIC_COMMENT,
'disabled' => $pic->{'state'} eq 'I' });
$body .= "</td></tr>\n";
}
}
$body .= "</table>\n</td></tr>\n";
}
if ($piccount) {
$body .= "<tr><td></td><td align=left>&nbsp;<b><label for='nodefpic'>$ML{'.nodefault'}</label></b>&nbsp;";
$body .= LJ::html_check({ 'name' => 'defaultpic',
'value' => 0,
'type' => 'radio',
'selected' => ! $u->{'defaultpicid'},
'raw' => "id='nodefpic'" });
$body .= "</td><td>&nbsp;</td></tr>\n";
$body .= "<tr><td></td><td>&nbsp;" . LJ::html_submit('action:save', $ML{'.btn.save'}) . "</td></tr>\n";
$body .= "</table>";
$body .= "</form>";
} else {
$body .= "<?h1 $ML{'.nopics'} h1?><?p $ML{'.noneupload'} p?>";
}
}
# let users upload more pics
$body .= "<a name='upload'></a>";
if ($count < $max) {
if ($fotobilder) {
$body .= "<?h1 $ML{'.uploadheader.fb'} h1?>\n";
$body .= "<?p " . BML::ml('.uploaddesc.fb', {'aopts' => "href='$LJ::FB_SITEROOT'", 'sitename' => $LJ::FB_SITENAME}) . " p?>\n\n";
} else {
$body .= "<?h1 $ML{'.uploadheader'} h1?>\n";
$body .= "<?p $ML{'.uploaddesc'} p?>\n\n";
$body .= "<ul>\n";
foreach (qw(filesize imagesize fileformat)) {
$body .= "<li>" . $ML{".restriction.$_"} . "</li>\n";
}
# Keywords is a little different
$body .= "<li>";
if (defined $LJ::HELPURL{'upic_keywords'}) {
$body .= BML::ml('.restriction.keywords.faq', {'aopts' => "href='$LJ::HELPURL{'upic_keywords'}'"});
} else {
$body .= $ML{'.restriction.keywords'};
}
$body .= "</li>";
$body .= "</ul>\n";
}
# upload form
$body .= "<?standout \n";
$body .= "<form action='editpics.bml$getextra' method='post' " .
"enctype='multipart/form-data' style='display: inline;'>\n";
$body .= "<table>\n";
if ($fotobilder) {
$body .= "<tr><td colspan='3' align='center'>\n";
$body .= "<img src='$picurl' />";
my $url = LJ::CleanHTML::canonical_url($POST{'url'});
$body .= LJ::html_hidden('src', 'url', 'urlpic', $picurl, 'url', $url, 'ret' => $returl);
} else {
$body .= "<tr><td align='right'>\n";
$body .= LJ::html_check({ 'type' => 'radio', 'name' => 'src', 'id' => 'radio_file',
'value' => 'file', 'selected' => '1',
'accesskey' => $ML{'.fromfile.key'} });
$body .= "</td><td align='right'>";
$body .= "<label for='radio_file'>$ML{'.fromfile'}</label></td>";
$body .= "<td align='left'><input type='file' name='userpic' size='28' /></td></tr>\n";
$body .= "<tr><td align='right'>";
$body .= LJ::html_check({ 'type' => 'radio', 'name' => 'src', 'value' => 'url',
'id' => 'radio_url', 'accesskey' => $ML{'.fromurl.key'} });
$body .= "</td><td align='right'>";
$body .= "<label for='radio_url'>$ML{'.fromurl'}</label></td><td align='left'>";
$body .= LJ::html_text({ 'name' => 'urlpic', 'size' => '40' }) . "</td></tr>\n";
}
$body .= "<tr><td colspan='3' align='center'><hr></td></tr>";
$body .= "<tr><td align='right'>";
$body .= LJ::help_icon('upic_keywords');
$body .= "</td><td align='right'><label for='keywords'>";
$body .= "$ML{'.label.keywords'}</label></td><td align='left'>";
$body .= LJ::html_text({ 'name' => 'keywords', 'size' => '40' });
$body .= "</td></tr>";
if ($u->{'dversion'} > 6) {
$body .= "<tr><td align='right'>";
$body .= LJ::help_icon('upic_comments');
$body .= "</td><td align='right'><label for='comments'>$ML{'.label.comment'}</label></td><td align='left'>";
my $comments = $POST{'comments'} if $fotobilder;
$body .= LJ::html_text({ 'name' => 'comments', 'size' => '40', 'maxlength' => LJ::CMAX_UPIC_COMMENT, 'value', $comments });
$body .= "</td></tr>";
}
$body .= "<tr><td colspan='2'>&nbsp;</td><td align='left'>";
$body .= LJ::html_check({ 'type' => 'checkbox',
'name' => 'make_default',
'id' => 'make_default',
'selected' => '1', 'value' => '1',
'accesskey' => $ML{'.makedefault.key'} });
$body .= "<label for='make_default'>$ML{'.makedefault'}</label></td></tr>\n";
$body .= "<tr><td colspan='3' align='center'><br />" .LJ::html_submit(undef, $ML{'.btn.proceed'}) . "</td></tr>\n";
$body .= "</table>\n</form>\n";
} else {
$body .= "<?standout \n";
$body .= BML::ml(".error.toomanypics3", { 'max' => $max });
}
$body .= " standout?>\n\n";
return;
}
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?><?_c <LJDEP>
link: htdocs/login.bml, htdocs/allpics.bml
post: htdocs/editpics.bml
</LJDEP> _c?>

124
livejournal/htdocs/edittags.bml Executable file
View File

@@ -0,0 +1,124 @@
<?page
title=><?_code $ML{'.title'} _code?>
body<=
<?_code
{
use strict;
use vars qw($GET $POST);
my $remote = LJ::get_remote();
return "<?needlogin?>" unless $remote;
my $err = sub { return "<?h1 $ML{'Error'} h1?><?p $_[0] p?>"; };
return $err->($ML{'.disabled'})
if $LJ::DISABLED{tags};
my ($ret, $msg);
return $err->($ML{'.invalid.link'})
unless LJ::did_post() || ($GET{journal} && $GET{itemid});
my $journal = $GET{journal} || $POST{journal};
my $u = LJ::load_user($journal);
return $err->($ML{'.invalid.journal'}) unless $u;
my $ditemid = ($GET{itemid} || $POST{itemid})+0;
my $anum = $ditemid % 256;
my $jitemid = $ditemid >> 8;
return $err->($ML{'.invalid.entry'}) unless $jitemid;
my $logrow = LJ::get_log2_row($u, $jitemid);
return $err->($ML{'.invalid.entry'}) unless $logrow;
return $err->($ML{'.invalid.entry'}) unless $logrow->{anum} == $anum;
return $err->($ML{'.invalid.notauthorized'})
unless LJ::can_view($remote, $logrow);
if (LJ::did_post()) {
return $err->($ML{'.invalid.link'})
unless LJ::check_form_auth();
LJ::Tags::update_logtags($u, $jitemid, {
set_string => $POST{edittags},
remote => $remote,
});
BML::redirect( LJ::journal_base($u) . "/$ditemid.html" );
#$msg = "<div class='update_good'>Tags successfully updated.</div>";
}
my $lt2 = LJ::get_logtext2($u, $jitemid);
my ($subj, $evt) = @{$lt2->{$jitemid} || []};
return $err->($ML{'.error.db'}) unless $evt;
LJ::CleanHTML::clean_subject(\$subj);
LJ::CleanHTML::clean_event(\$evt);
my $logtags = LJ::Tags::get_logtags($u, $jitemid);
my $usertags = LJ::Tags::get_usertags($u, { remote => $remote }) || {};
$logtags = $logtags->{$jitemid} || {};
my $logtagstr = join ', ', map { LJ::ejs($_) } sort values %$logtags;
$ret .= "<?p $ML{'.intro'} p?><br />";
$ret .= "<script type='text/javascript'> var cur_taglist = '$logtagstr'; </script>";
$ret .= '<table class="edittbl" cellpadding="0" cellspacing="0" width="50%">';
$ret .= "<tr><td class='l'>$ML{'.subject'}</td><td>$subj</td></tr>" if $subj;
$ret .= "<tr><td class='l'>$ML{'.entry'}</td><td>$evt</td></tr>";
$ret .= "<tr><td class='l'>&nbsp;</td><td>&nbsp</td></tr>"; # spacer
$ret .= "<tr><td class='l'>$ML{'.current'}</td>";
$ret .= '<form method="POST" action="/edittags.bml" id="edit_tagform">';
$ret .= LJ::form_auth();
$ret .= "<td class='sep'>";
if ( LJ::Tags::can_add_tags($u, $remote) ) {
$ret .= LJ::html_text(
{
name => 'edittags',
value => (join ', ', sort values %$logtags),
size => 40,
class => 'tagfield',
id => 'tagfield',
}
);
$ret .= '&nbsp;&nbsp;';
$ret .= LJ::html_submit( 'save', $ML{'.button.save'}, { class => 'btn' });
$ret .= $msg if $msg;
} else {
# no widgets
$ret .= $ML{'.permissions.none'};
}
$ret .= "</td></tr>";
$ret .= "<tr><td class='l'>$ML{'.users'}</td><td class='curtags'>";
if ( scalar keys %$usertags ) {
$ret .= "<select name='tags' multiple='multiple' class='tagbox_nohist' " .
"onChange='edit_tagselect(this)'>";
foreach (sort { $a->{name} cmp $b->{name} } values %$usertags) {
$ret .= "<option value='" . LJ::ehtml($_->{name}) . "'>" . LJ::ehtml($_->{name}) . "</option>";
}
$ret .= "</select>";
} else {
$ret .= "none"
}
$ret .= "<br /><br />";
$ret .= "$ML{'.permissions.add.yes'}<br />" if LJ::Tags::can_add_tags($u, $remote);
$ret .= "$ML{'.permissions.control.yes'}<br />" if LJ::Tags::can_control_tags($u, $remote);
$ret .= BML::ml('.view', { aopts => 'href="' . LJ::journal_base($u) . "/$ditemid.html" . '"' });
$ret .= "</td></tr>";
$ret .= '</table>';
$ret .= LJ::html_hidden('journal', $journal);
$ret .= LJ::html_hidden('itemid', $GET{itemid} || $POST{itemid});
$ret .= '</form>';
return $ret;
}
_code?>
<=body
head<=
<link rel='stylesheet' type='text/css' href='/styles/tags.css' />
<script type="text/javascript" src="/js/tags.js"></script>
<=head
page?>

109
livejournal/htdocs/export.bml Executable file
View File

@@ -0,0 +1,109 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
{
use strict;
use vars qw(%GET);
LJ::set_active_crumb('export');
my $remote = LJ::get_remote();
return "<?needlogin?>"
unless $remote;
my $ret;
my $authas = $GET{'authas'} || $remote->{'user'};
# no authentication needs to be done on this page, it's just a form anyway
# user switcher
$ret .= "<form action='export.bml' method='get'>\n";
$ret .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'} });
$ret .= "</form>\n\n";
$ret .= "<?h1 <?_ml .title _ml?> h1?>\n";
$ret .= "<?p <?_ml .description _ml?> p?>\n";
$ret .= "<form action='export_do.bml?authas=$authas' method='post'>\n";
# main form
$ret .= <<'HTMLBLOCK';
<?standout
<table>
<tr valign='top'><td><?_ml .label.what _ml?></td>
<td>
<select name="what" id="what">
<option value="journal"><?_ml .what.entries _ml?></option>
</select>
</td></tr>
<tr valign='middle'><td><?_ml .label.month _ml?></td>
<td>
<?_ml .label.month.year _ml?> <input name="year" size='4' maxlength='4' /> -
<?_ml .label.month.month _ml?> <input name="month" size='2' maxlength='2' />
</td></tr>
<tr valign='top'><td><?_ml .label.format _ml?></td>
<td>
<select name="format">
<option value="csv"><?_ml .format.csv _ml?></option>
<option value="xml"><?_ml .format.xml _ml?></option>
</select><br />
<input type='checkbox' name="header" id="header" checked='checked' /> <label for="header"><?_ml .label.header _ml?></label><br />
<?_ml .label.encoding _ml?>
HTMLBLOCK
# unicode encoding selection
if ($LJ::UNICODE) {
my (%encodings, %encnames);
LJ::load_codes({ "encoding" => \%encodings } );
LJ::load_codes({ "encname" => \%encnames } );
my $selected = 0;
foreach my $id (keys %encodings) {
$selected = $id if lc($encodings{$id}) eq 'utf-8';
delete $encnames{$id} if lc($encodings{$id}) eq 'none';
}
$ret .= LJ::html_select({'name'=>'encid', 'selected'=>$selected},
map { $_, $encnames{$_} } sort keys %encnames);
} else {
$ret .= "<input type='text' name='encoding' size='15' maxlength='15' value='iso-8859-1' />";
}
$ret .= "<br />\n";
$ret .= <<'HTMLBLOCK';
<input type='checkbox' name="notranslation" id="notranslation" /> <label for="notranslation"><?_ml .label.notranslation _ml?></label><br />
</td></tr>
<tr><td colspan='2'><hr /></td></tr>
<tr valign='top'><td><?_ml .fields _ml?></td>
<td>
<input type='checkbox' name="field_itemid" id="field_itemid" checked='checked' /> <label for="field_itemid"><?_ml .label.field.itemid _ml?></label><br />
<input type='checkbox' name="field_eventtime" id="field_eventtime" checked='checked' /> <label for="field_eventtime"><?_ml .label.field.eventtime _ml?></label><br />
<input type='checkbox' name="field_logtime" id="field_logtime" checked='checked' /> <label for="field_logtime"><?_ml .label.field.logtime _ml?></label><br />
<input type='checkbox' name="field_subject" id="field_subject" checked='checked' /> <label for="field_subject"><?_ml .label.field.subject _ml?></label><br />
<input type='checkbox' name="field_event" id="field_event" checked='checked' /> <label for="field_event"><?_ml .label.field.event _ml?></label><br />
<input type='checkbox' name="field_security" id="field_security" checked='checked' /> <label for="field_security"><?_ml .label.field.security _ml?></label><br />
<input type='checkbox' name="field_allowmask" id="field_allowmask" checked='checked' /> <label for="field_allowmask"><?_ml .label.field.allowmask _ml?></label><br />
<input type='checkbox' name='field_currents' id="field_currents" checked='checked' /> <label for="field_currents"><?_ml .label.field.currents _ml?></label><br />
</td></tr>
<tr><td></td><td><input type="submit" value="<?_ml .btn.proceed _ml?>" /></td></tr>
</table>
standout?>
</form>
<?h1 <?_ml lostinfo.head _ml?> h1?>
<?p <?_ml lostinfo.text _ml?> p?>
HTMLBLOCK
}
_code?>
<=body
page?><?_c <LJDEP>
post: htdocs/export_do.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,158 @@
<?_code
{
use strict;
use vars qw(%GET);
my $req = shift;
my $r = $req->{'r'};
my $remote = LJ::get_remote();
return "<?needlogin?>" unless $remote;
my $authas = $GET{'authas'} || $remote->{'user'};
my $u = LJ::get_authas_user($authas);
return LJ::bad_input($ML{'error.invalidauth'}) unless $u;
my @errors = ();
my $dbcr = LJ::get_cluster_reader($u);
push @errors, $ML{'error.nodb'} unless $dbcr;
# don't let people hit us with silly GET attacks
push @errors, "This page can't be viewed except via POST."
if BML::get_client_header('Referer') && !LJ::did_post();
my $mode = $GET{get};
push @errors, "Invalid mode."
unless $mode =~ m/^comment_(?:meta|body)$/;
# error stuff
return LJ::bad_input(@errors) if @errors;
# from now on, we manage our own output
BML::suppress_headers();
BML::suppress_content();
# print top
$r->content_type("text/xml; charset=utf-8");
$r->send_http_header();
$r->print("<?xml version=\"1.0\" encoding='utf-8'?>\n<livejournal>\n");
# startid specified?
my $gather = $mode eq 'comment_meta' ? 10000 : 1000;
my $startid = $GET{startid}+0;
my $endid = $startid + $gather;
# get metadata
my $rows = $dbcr->selectall_arrayref('SELECT jtalkid, nodeid, parenttalkid, posterid, state, datepost ' .
"FROM talk2 WHERE nodetype = 'L' AND journalid = ? AND " .
" jtalkid >= $startid AND jtalkid < $endid",
undef, $u->{userid});
# now let's gather them all together while making a list of posterids
my %posterids;
my %comments;
foreach my $r (@{$rows || []}) {
$comments{$r->[0]} = {
nodeid => $r->[1],
parenttalkid => $r->[2],
posterid => $r->[3],
state => $r->[4],
datepost => $r->[5],
};
$posterids{$r->[3]} = 1 if $r->[3]; # don't include 0 (anonymous)
}
# now we have two choices: comments themselves or metadata
if ($mode eq 'comment_meta') {
# meta data is easy :)
my $max = $dbcr->selectrow_array('SELECT MAX(jtalkid) FROM talk2 ' .
"WHERE journalid = ? AND nodetype = 'L'",
undef, $u->{userid});
$max += 0;
$r->print("<maxid>$max</maxid>\n");
# load posterids
my $us = LJ::load_userids(keys %posterids);
# now spit out the metadata
$r->print("<comments>\n");
while (my ($id, $data) = each %comments) {
my $ret = "<comment id='$id'";
$ret .= " posterid='$data->{posterid}'" if $data->{posterid};
$ret .= " state='$data->{state}'" if $data->{state} ne 'A';
$ret .= " />\n";
$r->print($ret);
}
$r->print("</comments>\n<usermaps>\n");
# now spit out usermap
my $ret = '';
while (my ($id, $user) = each %$us) {
$ret .= "<usermap id='$id' user='$user->{user}' />\n";
}
$r->print($ret);
$r->print("</usermaps>\n");
# comment data also presented in glorious XML:
} elsif ($mode eq 'comment_body') {
# get real comments from startid to a limit of 10k data, however far that takes us
my @ids = sort { $a <=> $b } keys %comments;
# call a load to get comment text
my $texts = LJ::get_talktext2($u, @ids);
# get props if we need to
my $props = {};
if ($GET{'props'}) {
LJ::load_talk_props2($u->{userid}, \@ids, $props);
}
# now start spitting out data
$r->print("<comments>\n");
foreach my $id (@ids) {
# get text for this comment
my $data = $comments{$id};
my $text = $texts->{$id};
my ($subject, $body) = @{$text || []};
# only spit out valid UTF8, and make sure it fits in XML, and uncompress it
LJ::text_uncompress(\$body);
LJ::text_out(\$subject);
LJ::text_out(\$body);
$subject = LJ::exml($subject);
$body = LJ::exml($body);
# setup the date to be GMT and formatted per W3C specs
my $date = LJ::mysqldate_to_time($data->{datepost});
$date = LJ::time_to_w3c($date, 'Z');
# print the data
my $ret = "<comment id='$id' jitemid='$data->{nodeid}'";
$ret .= " posterid='$data->{posterid}'" if $data->{posterid};
$ret .= " state='$data->{state}'" if $data->{state} ne 'A';
$ret .= " parentid='$data->{parenttalkid}'" if $data->{parenttalkid};
if ($data->{state} eq 'D') {
$ret .= " />\n";
} else {
$ret .= ">\n";
$ret .= "<subject>$subject</subject>\n" if $subject;
$ret .= "<body>$body</body>\n" if $body;
$ret .= "<date>$date</date>\n";
foreach my $propkey (keys %{$props->{$id} || {}}) {
$ret .= "<property name='$propkey'>";
$ret .= LJ::exml($props->{$id}->{$propkey});
$ret .= "</property>\n";
}
$ret .= "</comment>\n";
}
$r->print($ret);
}
$r->print("</comments>\n");
}
# all done
$r->print("</livejournal>\n");
}
_code?><?_c <LJDEP>
</LJDEP> _c?>

203
livejournal/htdocs/export_do.bml Executable file
View File

@@ -0,0 +1,203 @@
<?_code
LJ::set_active_crumb('export');
my $req = shift;
my $r = $req->{'r'};
my $remote = LJ::get_remote();
return "<?needlogin?>"
unless $remote;
my $authas = $GET{'authas'} || $remote->{'user'};
my $u = LJ::get_authas_user($authas);
return LJ::bad_input($ML{'error.invalidauth'})
unless $u;
my @errors = ();
my $year = $POST{'year'}+0;
my $month = $POST{'month'}+0;
my $dbcr = LJ::get_cluster_reader($u);
push @errors, $ML{'error.nodb'} unless $dbcr;
my $encoding;
if ($POST{'encid'}) {
my %encodings;
LJ::load_codes({ "encoding" => \%encodings } );
$encoding = $encodings{$POST{'encid'}};
}
$encoding ||= $POST{'encoding'};
$encoding ||= $LJ::UNICODE ? 'utf-8' : 'iso-8859-1';
if ($LJ::UNICODE && lc($encoding) ne "utf-8" &&
! Unicode::MapUTF8::utf8_supported_charset($encoding)) {
push @errors, $ML{'.error.encoding'};
}
if (@errors) {
return LJ::bad_input(@errors);
}
# from now on, we manage our own output
BML::suppress_headers();
BML::suppress_content();
my $opts = {}; # information needed by printing routines
##### figure out what fields we're exporting
my @fields;
foreach my $f (qw(itemid eventtime logtime subject event security allowmask)) {
if ($POST{"field_${f}"}) {
push @fields, $f;
}
}
if ($POST{'field_currents'}) {
push @fields, ("current_music", "current_mood");
$opts->{'currents'} = 1;
}
#### do file-format specific initialization
if ($POST{'format'} eq "csv") {
$opts->{'format'} = "csv";
$r->content_type("text/plain");
$r->send_http_header();
if ($POST{'header'}) {
$r->print(join(",",@fields) . "\n");
}
}
if ($POST{'format'} eq "xml") {
$opts->{'format'} = "xml";
my $lenc = lc($encoding);
$r->content_type("text/xml; charset=$lenc");
$r->send_http_header();
$r->print("<?xml version=\"1.0\" encoding='$lenc'?>\n");
$r->print("<livejournal>\n");
}
$opts->{'fields'} = \@fields;
$opts->{'encoding'} = $encoding;
$opts->{'notranslation'} = 1
if $POST{'notranslation'};
$sth = $dbcr->prepare("SELECT jitemid, anum, eventtime, logtime, security, allowmask FROM log2 ".
"WHERE journalid=$u->{'userid'} AND year=$year AND month=$month");
$sth->execute;
if ($dbcr->err) { $r->print($dbcr->errstr); return; }
my @buffer;
while ($_ = $sth->fetchrow_hashref) {
$_->{'ritemid'} = $_->{'jitemid'} || $_->{'itemid'};
$_->{'itemid'} = $_->{'jitemid'} * 256 + $_->{'anum'} if $_->{'jitemid'};
push @buffer, $_;
if (@buffer == 20) {
load_and_dump_buffer($u, \@buffer, $opts);
@buffer = ();
}
}
load_and_dump_buffer($u, \@buffer, $opts);
if ($opts->{'format'} eq "xml") {
$r->print("</livejournal>\n");
}
return;
sub load_and_dump_buffer
{
my ($u, $buf, $opts) = @_;
my $lt;
my %props;
my @ids = map { $_->{'ritemid'} } @{$buf};
$lt = LJ::get_logtext2($u, @ids);
LJ::load_log_props2($dbcr, $u->{'userid'}, \@ids, \%props);
foreach my $e (@{$buf}) {
$e->{'subject'} = $lt->{$e->{'ritemid'}}->[0];
$e->{'event'} = $lt->{$e->{'ritemid'}}->[1];
my $eprops = $props{$e->{'ritemid'}};
# convert to UTF-8 if necessary
if ($LJ::UNICODE && $eprops->{'unknown8bit'} && !$opts->{'notranslation'}) {
my $error;
$e->{'subject'} = LJ::text_convert($e->{'subject'}, $u, \$error);
$e->{'event'} = LJ::text_convert($e->{'event'}, $u, \$error);
foreach (keys %{$eprops}) {
$eprops->{$_} = LJ::text_convert($eprops->{$_}, $u, \$error);
}
}
if ($opts->{'currents'}) {
$e->{'current_music'} = $eprops->{'current_music'};
$e->{'current_mood'} = $eprops->{'current_mood'};
if ($eprops->{'current_moodid'}) {
my $mood = LJ::mood_name($eprops->{'current_moodid'})
if $eprops->{'current_moodid'};
$e->{'current_mood'} = $mood if $mood;
}
}
my $entry = dump_entry($e, $opts);
# now translate this to the chosen encoding but only if this is a
# Unicode environment. In a pre-Unicode environment the chosen encoding
# is merely a label.
if ($LJ::UNICODE && lc($opts->{'encoding'}) ne 'utf-8' && !$opts->{'notranslation'}) {
$entry = Unicode::MapUTF8::from_utf8({-string=>$entry,
-charset=>$opts->{'encoding'}});
}
$r->print($entry);
}
}
sub dump_entry
{
my $e = shift;
my $opts = shift;
my $format = $opts->{'format'};
my $entry = "";
my @vals = ();
if ($format eq "xml") {
$entry .= "<entry>\n";
}
foreach my $f (@{$opts->{'fields'}})
{
my $v = $e->{$f};
if ($format eq "csv") {
if ($v =~ /[\"\n\,]/) {
$v =~ s/\"/\"\"/g;
$v = "\"$v\"";
}
}
if ($format eq "xml") {
$v = LJ::exml($v);
}
push @vals, $v;
}
if ($format eq "csv") {
$entry .= join(",", @vals) . "\n";
}
if ($format eq "xml") {
foreach my $f (@{$opts->{'fields'}}) {
my $v = shift @vals;
$entry .= "<$f>" . $v . "</$f>\n";
}
$entry .= "</entry>\n";
}
return $entry;
}
_code?><?_c <LJDEP>
</LJDEP> _c?>

View File

@@ -0,0 +1 @@
This file exists to make sure the directory is created.

View File

@@ -0,0 +1,209 @@
<?_code
my $remote = LJ::get_remote();
my $user = $FORM{'user'};
my $u = LJ::load_user($user);
my $userid = $u->{userid};
$body = "";
LJ::set_active_crumb('addfriends');
unless ($remote)
{
$title = $ML{'.error1.title'};
$body = "<?h1 $ML{'.error1.header'} h1?><?p $ML{'.error1.text'} p?>";
return;
}
unless ($user && $userid)
{
$title = $ML{'Error'};
$body = $ML{'.error2.text'};
return;
}
if ($FORM{'mode'} eq "add")
{
unless (LJ::did_post()) {
$title = $ML{'Error'};
$body = "<?h1 $ML{'Error'} h1?><?p <?requirepost?> p?>";
return;
}
unless ($remote->{'userid'} == $FORM{'remid'}) {
$title = $ML{'Error'};
$body = "<?h1 $ML{'Error'} h1?><?p Session info changed. Try again. p?>";
return;
}
my $gmask = 1;
foreach my $bit (1..30) {
next unless $FORM{"bit_$bit"};
$gmask |= (1 << $bit);
}
my $req = {
"user" => $remote->{'user'},
"mode" => "editfriends",
"ver" => $LJ::PROTOCOL_VER,
};
if ($FORM{'action:delete'}) {
$req->{"editfriend_delete_$user"} = 1;
} else {
$req->{"editfriend_add_1_user"} = $user;
$req->{"editfriend_add_1_fg"} = $FORM{'editfriend_add_1_fg'};
$req->{"editfriend_add_1_bg"} = $FORM{'editfriend_add_1_bg'};
$req->{"editfriend_add_1_groupmask"} = $gmask;
}
my %res = ();
LJ::do_request($req, \%res,
{ "noauth" => 1, "userid" => $remote->{'userid'} } );
if ($res{'success'} eq "OK")
{
if ($FORM{'action:delete'}) {
$title = $ML{'.remove.title'};
$body = "<?h1 $ML{'.remove.header'} h1?><?p " .
BML::ml('.remove.text', {'user'=>$user,
'ljuser' => LJ::ljuser($u),
'url'=>"$LJ::SITEROOT/users/$remote->{'user'}/friends"}) .
" p?>";
} else {
$title = $ML{'.add.title'};
$body = "<?h1 $ML{'.add.header'} h1?><?p " .
BML::ml('.add.text', { 'user' => $user,
'ljuser' => LJ::ljuser($u),
'url' => "$LJ::SITEROOT/users/$remote->{'user'}/friends", }) .
" p?>";
}
} else {
$title = $ML{'Error'};
$body = "<?h1 $ML{'Error'} h1?><?p $res{'errmsg'} p?>";
}
return;
}
# check to see if user is already a friend.
# TAG:fr:bml_friends_add:check_is_friend
my $dbr = LJ::get_db_reader();
$sth = $dbr->prepare("SELECT * FROM friends WHERE userid=$remote->{'userid'} AND friendid=$userid");
$sth->execute;
my $fr = $sth->fetchrow_hashref;
if ($fr) {
$title .= $ML{'.error3.title'};
$body .= "<?h1 $ML{'.error3.title'} h1?><?p " .
BML::ml('.error3.text', {'user'=>$user}) . " p?>";
} else {
# was this a syndicated add?
if ($u->{journaltype} eq 'Y') {
$title = $ML{'.confirm.syn.title'};
$body .= "<?h1 " . BML::ml('.confirm.syn.header', { user => $user }) . " h1?>";
} else {
$title .= $ML{'.confirm.title'};
$body .= "<?h1 " . BML::ml(".confirm.header", { 'user' => $user }) . " h1?>";
}
$body .= "<?p " . BML::ml(".confirm.text", { 'user' => $user }) . " p?>";
}
$body .= "<form method='post' action='add.bml'>";
$body .= LJ::html_hidden(mode => 'add',
user => $user,
remid => $remote->{userid});
if ($fr) {
$body .= "<center><input type='submit' value=\"$ML{'.btn.modify'}\">";
$body .= " - <input type='submit' name='action:delete' value=\"$ML{'.btn.remove'}\"></center>";
} else {
$body .= "<center><input type='submit' value=\"" .
BML::ml('.btn.add', {'user'=>$user}) . "\"></center>";
}
## let them pick friend groups
$body .= "<?h1 $ML{'.groups.header'} h1?><?p $ML{'.groups.text'} p?><blockquote>";
my $err;
my $greq = LJ::Protocol::do_request("getfriendgroups", {
'username' => $remote->{'user'},
'ver' => $LJ::PROTOCOL_VER,
}, \$err, { 'noauth' => 1 });
if (@{$greq->{'friendgroups'}}) {
foreach my $g (@{$greq->{'friendgroups'}}) {
my $ck = ($fr && ($fr->{'groupmask'} & (1 << $g->{'id'}))) ?
"checked='1'" : "";
# by default, newly added friends are in default view unless unchecked
$ck = "checked='1'" if (! $fr && $g->{'name'} eq "Default View");
$body .= "<input type='checkbox' value='1' name='bit_$g->{'id'}' $ck> ";
$body .= LJ::ehtml($g->{'name'}) . "<br />\n";
}
} else {
$body .= "<i>$ML{'.groups.nogroup'}</i>";
}
$body .= "</blockquote>";
## let them pick the colors
$body .= "<?h1 $ML{'.colors.header'} h1?><?p " .
BML::ml('.colors.text', {'user'=>$user}) . " p?>";
$ret = "";
$ret .= "<P><CENTER><TABLE CELLPADDING=4><TR><TD><B>$ML{'.colors.fg'}</B></TD><TD><B>$ML{'.colors.bg'}</B></TD></TR>\n";
my @color = ();
LJ::load_codes({ "color" => \@color });
my $sel = $fr || { 'fgcolor' => hex '000000',
'bgcolor' => hex 'FFFFFF', };
$ret .= "<TR>";
$ret .= "<TD><SELECT NAME=\"editfriend_add_1_fg\">";
foreach (@color) {
my $color_int = hex (substr($_->{'code'},1));
my $selected = $color_int eq $sel->{'fgcolor'} ? " SELECTED" : "";
$ret .= "<OPTION VALUE=\"$_->{'code'}\"$selected>$_->{'item'}\n";
}
$ret .= "</SELECT></TD>\n";
$ret .= "<TD><SELECT NAME=\"editfriend_add_1_bg\">";
foreach (@color) {
my $color_int = hex (substr($_->{'code'},1));
my $selected = $color_int eq $sel->{'bgcolor'} ? " SELECTED" : "";
$ret .= "<OPTION VALUE=\"$_->{'code'}\"$selected>$_->{'item'}\n";
}
$ret .= "</SELECT></TD>\n";
$ret .= "</TR>\n";
$ret .= "</TABLE>\n";
### color swatch
my $col = 0;
$ret .= "<P><TABLE border=0 cellspacing=0 cellpadding=0>";
foreach (@color) {
if ($col==0) { $ret .= "<TR>\n"; }
$col++;
my $ecolor = LJ::ehtml($_->{'item'});
$ret .= "<TD bgcolor=$_->{'code'}><IMG SRC=\"/img/dot.gif\" WIDTH=14 HEIGHT=14 TITLE=\"$ecolor\" ALT=\"$ecolor\"></TD>\n";
if ($col==23) { $ret .= "</TR>\n"; $col==0; }
}
if ($col) { $ret .= "</TR>\n"; $col==0; }
$ret .= "</TABLE>\n";
$ret .= "<FONT SIZE=-2 FACE=\"Arial,Helvetica\">$ML{'.colors.hover'}</FONT>";
$ret .= "</CENTER>\n";
$body .= $ret;
$body .= "</form>";
return;
_code?>
<?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?><?_c <LJDEP>
link: htdocs/login.bml, htdocs/create.bml, htdocs/friends/edit.bml, htdocs/users
img: htdocs/img/dot.gif
post: htdocs/friends/add.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,290 @@
<?page
title=><?_ML .title _ML?>
body<=
<?_code
{
use strict;
use vars qw(%GET %POST);
return LJ::server_down_html() if ($LJ::SERVER_DOWN);
return "<?badinput?>" unless LJ::text_in(\%POST);
LJ::set_active_crumb('editfriends');
# this file used to be edit_do.bml, but edit.bml died, so we moved this
# functionality to edit.bml, but we don't want the translators to have to
# retranslate all of the phrases, so we're still using /edit_do.bml scope
BML::set_language_scope("/friends/edit_do.bml");
my $remote = LJ::get_remote();
return LJ::bad_input($ML{'error.noremote'})
unless $remote;
my $authas = $GET{'authas'} || $remote->{'user'};
my $getextra = $authas ne $remote->{'user'} ? "?authas=$authas" : '';
my $u = LJ::get_authas_user($authas);
return LJ::bad_input("You could not be authenticated as the specified user.")
unless $u;
return BML::redirect("$LJ::SITEROOT/community/members.bml?comm=$u->{'user'}")
if $u->{'journaltype'} eq 'C';
return LJ::bad_input("Cannot modify friends of this journal type.")
unless $u->{'journaltype'} =~ /^[PSI]$/;
my $ret;
# no post, show edit form
unless (LJ::did_post()) {
# user switcher
$ret .= "<form action='edit.bml' method='get'>\n";
$ret .= LJ::make_authas_select($remote,
{ 'authas' => $GET{'authas'},
'type' => ['P', 'S'] });
$ret .= "</form>\n\n";
$ret .= "<form method='post' name='editFriends' action='edit.bml$getextra'>\n";
### who has you defined as a friend?
my %res = ();
LJ::do_request({
"user" => $u->{'user'},
"mode" => "friendof",
"ver" => $LJ::PROTOCOL_VER,
}, \%res, { "noauth" => 1, 'u' => $u });
### who do you have defined as a friend?
my %resf = ();
LJ::do_request({
"user" => $u->{'user'},
"mode" => "getfriends",
"ver" => $LJ::PROTOCOL_VER,
}, \%resf, { "noauth" => 1, 'u' => $u });
# build hash for checking if user is friend
my %isfriend = map { $resf{"friend_${_}_user"}, 1 } 1..$resf{'friend_count'};
### table of friend-ofs
if ($res{'friendof_count'}) {
$ret .= "<?h1 $ML{'.fellowfriends.head'} h1?><?p $ML{'.fellowfriends.text'} p?>\n";
$ret .= "<table style='margin-top: 20px;' align='center'>\n";
$ret .= "<tr bgcolor='<?emcolor?>'><td width='150'><b>$ML{'.user'}</b></td>";
$ret .= "<td width='200'><b>$ML{'.name'}</b></td>";
$ret .= "<td align='center'><b>$ML{'.opt.addtolist'}</b></td></tr>";
my $friendnum = 6; # 1-5 are new friends from the add form
for my $i (1..$res{'friendof_count'}) {
my $fo_user = $res{"friendof_${i}_user"};
$ret .= "<tr align='left'><td>" . LJ::ljuser($fo_user) . "</td>";
$ret .= "<td><label for='editfriend_add_${friendnum}_user'>";
$ret .= LJ::ehtml($res{"friendof_${i}_name"}) . "</label></td><td align='center'>";
my $dis = $isfriend{$fo_user} ? 1 : 0;
$ret .= LJ::html_check({ 'type' => 'check',
'name' => "editfriend_add_${friendnum}_user",
'id' => "editfriend_add_${friendnum}_user",
'value' => $fo_user, 'disabled' => $dis });
$friendnum++;
$ret .= "</td></tr>";
}
$ret .= "</table>";
}
### table of friends
if ($resf{'friend_count'}) {
$ret .= "<?h1 $ML{'.yourfriends.head'} h1?><?p $ML{'.yourfriends.text'} p?>\n";
$ret .= "<p align='center'><table style='margin-top: 20px;' align='center'>";
$ret .= "<tr bgcolor='<?emcolor?>'><td width='150'><b>$ML{'.friend'}</b></td>";
$ret .= "<td width='250'><b>$ML{'.name'}</b></td>";
$ret .= "<td align='center'><b>$ML{'.opt.delete'}</b></td></tr>\n";
foreach my $i (1..$resf{'friend_count'}) {
my $fruser = $resf{"friend_${i}_user"};
$isfriend{$fruser} = 1;
my $bgcolor = $resf{"friend_${i}_bg"} || "#FFFFFF";
my $fgcolor = $resf{"friend_${i}_fg"} || "#000000";
my $status = $resf{"friend_${i}_status"};
my $userlink = LJ::ljuser($fruser);
if ($status eq 'deleted' || $status eq 'purged' || $status eq 'suspended') {
$userlink = "<strike>$userlink</strike>";
}
$ret .= "<tr align='left'><td>$userlink</td>";
$ret .= "<td bgcolor='$bgcolor'><font color='$fgcolor'>";
$ret .= "<label for='editfriend_delete_${fruser}'>";
$ret .= LJ::ehtml($resf{"friend_${i}_name"}) . "</label>";
$ret .= "</font></td><td align='center'>";
$ret .= LJ::html_check({ 'type' => 'check',
'name' => "editfriend_delete_${fruser}",
'id' => "editfriend_delete_${fruser}"});
$ret .= "</td></tr>\n";
}
$ret .= "</table></p>\n\n";
} else {
$ret .= "<?h1 $ML{'.nofriends.head'} h1?><?p $ML{'.nofriends.text'} p?>\n\n";
}
### add friends
$ret .= "<?h1 $ML{'.addfriends.head'} h1?><?p $ML{'.addfriends.text'} p?>\n";
$ret .= "<p align='center'><input type='button' value='" . LJ::ehtml($ML{'.btn.toggle'}) . "' ";
$ret .= "onClick='togglePreview(); return true;'></p>\n";
$ret .= "<table cellpadding='4' align='center'>\n";
$ret .= "<tr><td><b>$ML{'.friend'}</b></td><td><b>$ML{'.foreground'}</b></td><td><b>$ML{'.background'}</b></td></tr>\n";
# load the colors
my @color = ();
LJ::load_codes({ "color" => \@color });
foreach my $i (1..5) {
$ret .= "<tr><td>";
$ret .= LJ::html_text({ 'name' => "editfriend_add_${i}_user",
'size' => '15', 'maxlength' => '15',
'onchange' => "updatePreview(); return true;",
'onfocus' => "setFriend($i);" });
$ret .= "</td><td>";
$ret .= LJ::html_select({ 'name' => "editfriend_add_${i}_fg",
'selected' => '#000000',
'onchange' => "updatePreview(); return true;",
'onfocus' => "setFriend($i);" },
map { lc($_->{'code'}), $_->{'item'} } @color );
$ret .= "</td><td>";
$ret .= LJ::html_select({ 'name' => "editfriend_add_${i}_bg",
'selected' => '#ffffff',
'onchange' => "updatePreview(); return true;",
'onfocus' => "setFriend($i);" },
map { lc($_->{'code'}), $_->{'item'} } @color );
$ret .= "</td></tr>\n";
}
$ret .= "</table>";
### color swatch
my $col = 0;
$ret .= "<table border='0' cellspacing='0' cellpadding='0' align='center'>";
foreach (@color) {
if ($col==0) { $ret .= "<tr>\n"; }
$col++;
my $ecolor = LJ::ehtml($_->{'item'});
$ret .= "<td bgcolor='$_->{'code'}'><img src='/img/dot.gif' width='14' height='14' title='$ecolor' alt='$ecolor' /></td>";
if ($col==23) { $ret .= "</tr>\n"; $col==0; }
}
if ($col) { $ret .= "</tr>\n"; $col==0; }
$ret .= "<tr><td colspan='23' align='center'><font size='-2' face='Arial,Helvetica'>($ML{'.hover'})</font></td></tr>";
$ret .= "</table>";
$ret .= "<p>$ML{'.needmore'}</p>\n";
### ending submit block
$ret .= "<?h1 $ML{'.done.head'} h1?><?p $ML{'.done.text'} p?>\n";
$ret .= "<?standout " . LJ::html_submit($ML{'.btn.save'}) . " standout?>\n";
$ret .= "</form>\n";
return $ret;
}
# if they did a post, then process their changes
if (LJ::did_post()) {
my %request = ();
$request{'mode'} = "editfriends";
$request{'ver'} = $LJ::PROTOCOL_VER;
$request{'user'} = $u->{'user'};
foreach (grep { /^editfriend_/ } keys %POST) {
$request{$_} = $POST{$_};
}
my %response = ();
LJ::do_request(\%request, \%response, { 'noauth' => 1, 'u' => $u });
if ($response{'success'} eq "OK") {
# tell the user all is well
return "<?h1 $ML{'.success.head'} h1?><?p ".BML::ml(".success.text", {'url' => LJ::journal_base($u) . "/friends"})." p?>";
} else {
return "<?h1 $ML{'Error'} h1?><?p $ML{'.error.updating'} <ul><li><b>$response{'errmsg'}</b></ul> p?>\n";
}
}
return $ML{'error.unknownmode'};
}
_code?>
<=body
head<=
<?_code
{
use strict;
BML::set_language_scope("/friends/edit_do.bml");
my %ejs = map { $_, LJ::ejs($ML{".$_"}) } qw(mrcolor viewer textcolor bgcolor btn.close);
return qq{
<script language="JavaScript"><!--
previewOn = 0;
lastFriend = 0;
function setFriend (curfriend)
{
lastFriend = curfriend;
}
function togglePreview()
{
if (previewOn==0 || winPreview.closed) {
winPreview = window.open("", "preview", "toolbar=0,location=0,directories=0,status=0,menubar=0,scrollbars=0,resizable=0,copyhistory=0,width=400,height=270");
previewOn = 1;
updatePreview();
} else {
winPreview.close();
previewOn = 0;
}
}
function updatePreview () {
if (previewOn == 0 || winPreview.closed) { return; }
frm = document.editFriends;
dropdown = frm["editfriend_add_"+lastFriend+"_fg"]
fg_color = dropdown.options[dropdown.selectedIndex].value;
fg_color_text = dropdown.options[dropdown.selectedIndex].text;
dropdown = frm["editfriend_add_"+lastFriend+"_bg"]
bg_color = dropdown.options[dropdown.selectedIndex].value;
bg_color_text = dropdown.options[dropdown.selectedIndex].text;
user_name = frm["editfriend_add_"+lastFriend+"_user"].value;
if (user_name.length==0) { user_name = "username"; }
d = winPreview.document;
d.open();
d.write("<html><head><title>$ejs{'mrcolor'}</title></head><body bgcolor='#ffffff' text='#000000'>");
d.write("<b><font face='Trebuchet MS, Arial, Helvetica' size='4' color='#000066'><i>$ejs{'viewer'}</i></font></b><hr />");
d.write("<br /><table width='350' align='center' cellpadding='5'><tr valign='middle'>");
d.write("<td width='80%'><b><font face='Arial, Helvetica' size='2'>");
d.write("$ejs{'textcolor'}&nbsp; <font color='#000066'>" + fg_color_text);
d.write("</font></b><br /></td><td width='20%' bgcolor=" + fg_color + ">&nbsp;</td>");
d.write("</tr><tr><td width='80%'><b><font face='Arial, Helvetica' size='2'>");
d.write("$ejs{'bgcolor'}&nbsp; <font color='#000066'>" + bg_color_text + "");
d.write("</font></b><br></td><td width='20%' bgcolor=" + bg_color + ">&nbsp;</td>");
d.write("</tr><tr><td><br /></tr><tr><td colspan='3' bgcolor=" + bg_color + "><font color=" + fg_color + ">");
d.write("<b>" + user_name + "</b></td></tr></table><br />");
d.write("<hr><form><div align='center'><input type='button' value='$ejs{'btn.close'}' onClick='self.close();'></div></form>");
d.write("</body></html>");
d.close();
}
// -->
</script>
}; # end qq{ }
}
_code?>
<=head
page?><?_c <LJDEP>
link: htdocs/create.bml, htdocs/lostinfo.bml
post: htdocs/friends/edit.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,425 @@
<?_code
{
use strict;
use vars qw(%GET %POST $title $body);
LJ::set_active_crumb('editfriendgrps');
$title = $ML{'.title'};
$body = "";
my $err = sub {
$title = "Error";
$body = LJ::bad_input(@_);
return;
};
# these are only used by the client-side for JS to play with.
# we delete them because they may contain embedded NULLs, which
# text_in won't like.
delete $POST{'list_in'};
delete $POST{'list_out'};
unless (LJ::text_in(\%POST)) {
$body = "<?badinput?>";
return;
}
my $remote = LJ::get_remote();
return $err->($ML{'error.noremote'})
unless $remote;
my $authas = $GET{'authas'} || $remote->{'user'};
my $u = LJ::get_authas_user($authas);
return $err->($ML{'error.invalidauth'})
unless $u;
# extra arguments for get requests
my $getextra = $authas ne $remote->{'user'} ? "?authas=$authas" : '';
if ($POST{'mode'} eq "save") {
my %res;
$POST{'mode'} = "editfriendgroups";
$POST{'user'} = $u->{'user'};
$POST{'ver'} = $LJ::PROTOCOL_VER;
LJ::do_request(\%POST, \%res, {
'u' => $u,
'noauth' => 1,
});
if ($res{'success'} eq "OK") {
$body .= "<?h1 $ML{'.saved.header'} h1?><?p $ML{'.saved.text'} p?>";
} else {
$body .= "<?h1 $ML{'Error'} h1?><?p $ML{'.error.text'} p?><?p $res{'errmsg'} p?>";
}
return;
}
my %res;
LJ::do_request({ 'mode' => 'getfriends',
'user' => $u->{'user'},
'ver' => $LJ::PROTOCOL_VER,
'includegroups' => 1 },
\%res, { 'u' => $u,
'noauth' => 1, });
my @num_used;
my @num_free;
# authas switcher form
$body .= "<form method='get' action='editgroups.bml'>\n";
$body .= LJ::make_authas_select($remote, { 'authas' => $GET{'authas'} }) . "\n";
$body .= "</form>\n\n";
$body .= "<?h1 $ML{'.title'} h1?><?p $ML{'.text'} p?><?p $ML{'.text.sec'} p?><p>";
$body .= "<form method='post' name='fg' action='editgroups.bml$getextra'>";
$body .= "<input type='hidden' name='mode' value='save' />";
for (my $i=1; $i<=30; $i++) {
my $sort = 255;
my $name = "";
my $public = 0;
if ($res{"frgrp_${i}_name"}) {
$sort = $res{"frgrp_${i}_sortorder"}+0;
$name = LJ::ehtml($res{"frgrp_${i}_name"});
$public = $res{"frgrp_${i}_public"}+0;
push @num_used, $i;
} else {
push @num_free, $i;
}
$body .= "<input type='hidden' name='efg_set_${i}_name' value='$name' />";
$body .= "<input type='hidden' name='efg_set_${i}_sort' value='$sort' />";
$body .= "<input type='hidden' name='efg_delete_${i}' value='0' />";
$body .= "<input type='hidden' name='efg_set_${i}_public' value='$public' />";
}
for (my $i=1; $i<=$res{'friend_count'}; $i++) {
my $user = $res{"friend_${i}_user"};
my $mask = $res{"friend_${i}_groupmask"} || 1;
$body .= "<input type='hidden' name='editfriend_groupmask_$user' value='$mask' />";
}
# escape strings for JavaScript
my %T = qw(public .group.public
rename .prompt.rename
newname .prompt.newname
delete .confirm.delete
max30 .error.max30
);
foreach (keys %T) { $T{$_} = LJ::ejs($ML{$T{$_}}); }
$body .= <<"END_JS";
<SCRIPT LANGUAGE="JavaScript"><!--
var selectedGroup = 0;
function eraseList (list)
{
while (list.length) {
list.options[0] = null;
}
}
function groupClicked ()
{
var selIndex;
var form = document.fg;
var grouplist = form.list_groups;
var inlist = form.list_in;
var outlist = form.list_out;
// figure out what they clicked, and bring their focus up to first free blank
selIndex = grouplist.selectedIndex;
if (selIndex == -1) { return; }
var groupname = grouplist.options[selIndex].text;
var newSelGroup = grouplist.options[selIndex].value;
if (newSelGroup == selectedGroup) { return; }
selectedGroup = newSelGroup;
// clears the other "not in" and "in" boxes
eraseList(inlist);
eraseList(outlist);
// iterate over all friends, putting them in one group or the other
var i;
for (i=0; i<form.elements.length; i++) {
var name = form.elements[i].name;
var mask = form.elements[i].value;
if (name.substring(0, 21) == "editfriend_groupmask_") {
var user = name.substring(21, name.length);
var list = mask & (1 << selectedGroup) ? inlist : outlist;
var optionName = new Option(user, user, false, false)
list.options[list.length] = optionName;
}
}
}
function moveItems (from, to, bitstatus)
{
var selindex;
while ((selindex=from.selectedIndex) != -1)
{
var i;
var item = new Option(from.options[selindex].text,
from.options[selindex].value,
false, true);
from.options[selindex] = null;
//to.options[to.options.length] = item;
// find spot to put new item
for (i=0; i<to.options.length && to.options[i].text < item.text; i++) { }
var newindex = i;
// move everything else down
for (i=to.options.length; i>newindex; i--) {
to.options[i] = new Option(to.options[i-1].text,
to.options[i-1].value,
false,
to.options[i-1].selected);
}
to.options[newindex] = item;
// turn the groupmask bit on or off
var user = item.value;
var element = document.fg["editfriend_groupmask_"+user];
var mask = element.value;
if (bitstatus) {
mask |= (1 << selectedGroup);
} else {
mask &= ~(1 << selectedGroup);
}
element.value = mask;
}
}
function moveIn ()
{
if (! selectedGroup) { return; }
var form = document.fg;
var inlist = form.list_in;
var outlist = form.list_out;
moveItems(document.fg.list_out, document.fg.list_in, true);
}
function moveOut ()
{
if (! selectedGroup) { return; }
moveItems(document.fg.list_in, document.fg.list_out, false);
}
function moveGroup (dir)
{
var list = document.fg.list_groups;
var selindex = list.selectedIndex;
if (selindex==-1) { return; }
var toindex = selindex+dir;
if (toindex < 0 || toindex >= list.options.length) { return; }
var selopt = new Option(list.options[selindex].text,
list.options[selindex].value,
false,
list.options[selindex].selected);
var toopt = new Option(list.options[toindex].text,
list.options[toindex].value,
false,
list.options[toindex].selected);
list.options[toindex] = selopt;
list.options[selindex] = toopt;
// stupid mozilla necessity:
list.selectedIndex = toindex;
setSortOrders();
}
function setSortOrders ()
{
var list = document.fg.list_groups;
// set all their sort orders now
var i;
for (i=0; i<list.options.length; i++) {
var item = list.options[i];
var key = "efg_set_"+item.value+"_sort";
document.fg[key].value = (i+1)*5;
}
}
function realName (name)
{
var rname = name;
var index = name.lastIndexOf(" $T{'public'}");
if (index != -1) {
rname = name.substr(0, index);
}
return rname;
}
function renameGroup ()
{
var list = document.fg.list_groups;
var selindex = list.selectedIndex;
if (selindex==-1) { return; }
var item = list.options[selindex];
var newtext = realName(item.text);
newtext = prompt("$T{'rename'}", newtext);
if (newtext==null || newtext == "") { return; }
var gnum = item.value;
document.fg["efg_set_"+gnum+"_name"].value = newtext;
if (document.fg["efg_set_"+gnum+"_public"].value == 1) {
newtext = newtext + " $T{'public'}";
}
item.text = newtext;
}
function deleteGroup ()
{
var list = document.fg.list_groups;
var selindex = list.selectedIndex;
if (selindex==-1) { return; }
var item = list.options[selindex];
var conf = confirm("$T{'delete'}");
if (!conf) { return; }
// mark it to be deleted later
var gnum = item.value;
document.fg["efg_delete_"+gnum].value = "1";
document.fg["efg_set_"+gnum+"_name"].value = "";
// as per the protocol documentation, unset bit on all friends
var i;
var form = document.fg;
for (i=0; i<form.elements.length; i++) {
var name = form.elements[i].name;
if (name.substring(0, 21) == "editfriend_groupmask_") {
var user = name.substring(21, name.length);
var mask = form.elements[i].value;
mask &= ~(1 << gnum);
form.elements[i].value = mask;
}
}
// clean up the UI
list.options[selindex] = null;
eraseList(document.fg.list_in);
eraseList(document.fg.list_out);
}
function makePublic ()
{
var list = document.fg.list_groups;
var selindex = list.selectedIndex;
if (selindex==-1) { return; }
var item = list.options[selindex];
var name = realName(item.text);
item.text = name + " $T{'public'}";
var gnum = item.value;
document.fg["efg_set_"+gnum+"_public"].value = "1";
}
function makePrivate ()
{
var list = document.fg.list_groups;
var selindex = list.selectedIndex;
if (selindex==-1) { return; }
var item = list.options[selindex];
var name = realName(item.text);
item.text = name;
var gnum = item.value;
document.fg["efg_set_"+gnum+"_public"].value = "0";
}
function newGroup ()
{
var form = document.fg;
var i;
var foundg = false;
for (i=1; i<=30; i++) {
if (form["efg_delete_"+i].value==1) { continue; }
if (form["efg_set_"+i+"_name"].value!="") { continue; }
foundg = true;
break;
}
if (! foundg) {
alert("$T{'max30'}");
return;
}
var gnum = i;
var groupname = prompt("$T{'newname'}", "");
if (groupname==null || groupname=="") { return; }
form["efg_set_"+gnum+"_name"].value = groupname;
var item = new Option(groupname, gnum, false, true);
var list = form.list_groups;
list.options[list.options.length] = item;
list.options.selectedIndex = list.options.length-1;
setSortOrders();
groupClicked();
}
// --></SCRIPT>
END_JS
$body .= "<table cellspacing='5'><tr valign='bottom'>";
$body .= "<td nowrap='1' colspan='2'><strong>$ML{'.yourgroups'}</strong></td>";
$body .= "<td nowrap='1' colspan='2'><strong>$ML{'.ingroup.not'}</strong></td>";
$body .= "<td nowrap='1'><strong>$ML{'.ingroup'}</strong></td></tr>";
$body .= "<tr valign='top'>";
$body .= "<td><select name='list_groups' style='width: 150px;' size='15' onchange='groupClicked();'>";
foreach my $num (sort { $res{"frgrp_${a}_sortorder"} <=>
$res{"frgrp_${b}_sortorder"} } @num_used) {
my $listname = $res{"frgrp_${num}_name"};
$listname .= " $ML{'.group.public'}" if $res{"frgrp_${num}_public"};
$body .= "<option value='$num'>" . LJ::ehtml($listname);
}
$body .= "</select></td>";
$body .= "<td valign='middle'>";
$body .= "<input type='button' value='$ML{'.btn.mv.up'}' onClick='moveGroup(-1);' /><br /><br />";
$body .= "<input type='button' value='$ML{'.btn.mv.down'}' onClick='moveGroup(1);' /></td>";
$body .= "<td><select name='list_out' multiple='1' size='15'>";
$body .= "<option value=''>---------------</option></select></td>";
$body .= "<td valign='middle'>";
$body .= "<input type='button' onClick='moveIn();' value='&gt;&gt;' /><br /><br />";
$body .= "<input type='button' onClick='moveOut();' value='&lt;&lt;' /></td>";
$body .= "<td><select name='list_in' multiple='1' size='15'>";
$body .= "<option value=''>---------------</option></select></td>";
$body .= "</tr><tr><td colspan='2'>";
$body .= "<nobr>";
$body .= "<input type='button' value='$ML{'.btn.ge.new'}' onClick='newGroup();' /> ";
$body .= "<input type='button' value='$ML{'.btn.ge.del'}' onClick='deleteGroup();' /> ";
$body .= "<input type='button' value='$ML{'.btn.ge.ren'}' onClick='renameGroup();' />";
$body .= "</nobr><br /><nobr>";
$body .= "<input type='button' value='$ML{'.btn.gs.public'}' onClick='makePublic();' /> ";
$body .= "<input type='button' value='$ML{'.btn.gs.private'}' onClick='makePrivate();' />";
$body .= "</nobr></td><td></td><td></td><td></td>";
$body .= "</tr></table>";
$body .= "<?h1 $ML{'.done.header'} h1?><?p $ML{'.done.text'} p?>";
$body .= "<?p <input type='submit' value='$ML{'.done.btn'}' /> p?>";
$body .= "</form>";
return;
}
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?><?_c <LJDEP>
link: htdocs/login.bml
post: htdocs/friends/editgroups.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,74 @@
<?_code
$title = $ML{'.title'};
$body = "";
LJ::set_active_crumb('friendsfilter');
if ($FORM{'mode'} eq "view")
{
my $user = lc($FORM{'user'});
my $filter = 0;
foreach my $k (keys %FORM) {
next unless ($k =~ /^bit_(\d+)$/);
my $bit = $1;
next if ($bit < 1 || $bit > 30);
$filter |= (1 << $bit);
}
my $extra = "?filter=$filter";
return BML::redirect("$LJ::SITEROOT/users/$user/friends${extra}");
}
my $remote = LJ::get_remote();
unless ($remote) { $body .= "<?needlogin?>"; return; }
my %res;
# FIXME: make this use LJ::Protocol::do_request
LJ::do_request({ 'mode' => 'getfriendgroups',
'ver' => $LJ::PROTOCOL_VER,
'user' => $remote->{'user'}, },
\%res, { 'noauth' => 1, 'userid' => $remote->{'userid'} });
unless ($res{'frgrp_maxnum'}) {
$body = "<?h1 $ML{'.error.nogroups.header'} h1?><?p $ML{'.error.nogroups'} p?>";
return;
}
my %group;
foreach $k (keys %res) {
if ($k =~ /^frgrp_(\d+)_name/) {
$group{$1}->{'name'} = $res{$k};
}
elsif ($k =~ /^frgrp_(\d+)_sortorder/) {
$group{$1}->{'sortorder'} = $res{$k};
}
}
$body .= "<?h1 $ML{'.select.header'} h1?><?p $ML{'.select'} p?>";
$body .= "<form method='post' style='display: inline' action='filter.bml'>\n";
$body .= LJ::html_hidden("user", $remote->{'user'},
"mode", "view");
$body .= "<div style='margin-left: 30px'>";
foreach my $g (sort { $group{$a}->{'sortorder'} <=> $group{$b}->{'sortorder'} } keys %group)
{
my $url = "$LJ::SITEROOT/users/$remote->{'user'}/friends/" . LJ::eurl($group{$g}->{'name'});
$body .= "<input type='checkbox' value='1' name=\"bit_$g\" /> <a href=\"$url\">" . LJ::ehtml($group{$g}->{'name'}) . "</a><br />\n";
}
$body .= "<input type='submit' value=\"$ML{'.submit'}\"> <input type='reset' value=\"$ML{'.reset'}\"></div>";
$body .= "<?p " . BML::ml('.editgroups', { 'link' => "<a href='editgroups.bml'>$ML{'/friends/editgroups.bml.title'}</a>" }) . " p?>";
$body .= "</form>";
return;
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?><?_c <LJDEP>
link: htdocs/users, htdocs/friends/editgroups.bml
post: htdocs/friends/filter.bml
</LJDEP> _c?>

View File

@@ -0,0 +1,27 @@
<?_info
nocache=>1
_info?><?_code
if (BML::get_path_info() =~ m!^/(\w+)\.dot(\.txt)?$!) {
BML::set_content_type("text/plain");
return "# dot file for $1:\n" . LJ::make_graphviz_dot_file($1);
}
my $u = LJ::load_user($FORM{'user'});
unless ($u) {
return "<?h1 Unknown user h1?><?p No user with that username. Sorry. p?>";
}
my $user = $u->{'user'};
my $ret = "";
$ret .= "User: <B>$user</B> (<A HREF=\"/userinfo.bml?user=$user\">user info</A>)<BR><I>If image doesn't load, press reload.</I><P>";
$ret .= "<A HREF=http://www.research.att.com/~north/cgi-bin/webdot.cgi/";
$ret .= "$LJ::SITEROOT/friends/graph.bml/$user.dot.map>";
$ret .= "<IMG SRC=http://www.research.att.com/~north/cgi-bin/webdot.cgi/";
$ret .= "$LJ::SITEROOT/friends/graph.bml/$user.dot.gif ismap border=0>";
$ret .= "</A>\n";
return $ret;
_code?>

View File

@@ -0,0 +1,34 @@
<?page
title=><?_ml .title _ml?>
body<=
<?_code
LJ::set_active_crumb('friends');
_code?>
<?p <?_ml .about _ml?> p?>
<?h1 <?_ml .tools _ml?> h1?>
<dl>
<dt><a href="edit.bml"><?_ml /friends/edit.bml.title _ml?></a></dt>
<dd><?_ml .edit.about _ml?></dd>
</dl>
<?p <?_ml .groups _ml?> p?>
<dl>
<dt><a href="editgroups.bml"><?_ml /friends/editgroups.bml.title _ml?></a></dt>
<dd><?_ml .editgroups.about _ml?></dd>
</dl>
<?p <?_ml .filter _ml?> p?>
<dl>
<dt><a href="filter.bml"><?_ml /friends/filter.bml.title _ml?></a></dt>
<dd><?_ml .filter.about _ml?></dd>
</dl>
<?h1 <?_ml .security.header _ml?> h1?>
<?p <?_ml .security _ml?> p?>
<ul>
<li><?_ml .security.only _ml?></li>
<li><?_ml .security.custom _ml?></li>
</ul>
<=body
page?><?_c <LJDEP>
link: htdocs/friends/edit.bml, htdocs/friends/editgroups.bml
link: htdocs/friends/filter.bml, htdocs/download/index.bml
</LJDEP> _c?>

60
livejournal/htdocs/go.bml Executable file
View File

@@ -0,0 +1,60 @@
<?_code
{
use strict;
use vars qw($title $body %GET %POST);
$title = $ML{'.defaulttitle'};
$body = $ML{'.defaultbody'};
# S2 Redirector
if ($POST{'redir_type'} eq "monthview") {
my $user = LJ::canonical_username($POST{'redir_user'});
my $vhost;
$vhost = $POST{'redir_vhost'} if $POST{'redir_vhost'}=~/users|tilde|community|front|other/;
if ($vhost eq "other") {
# FIXME: lookup their domain alias, and make vhost be "other:domain.com";
}
my $base = LJ::journal_base($user, $vhost);
return $ML{'.error.redirkey'} unless $POST{'redir_key'} =~/^(\d\d\d\d)(\d\d)$/;
my ($year, $month) = ($1, $2);
return BML::redirect("$base/$year/$month/");
}
# prev/next talkread links
my $itemid = $GET{'itemid'}+0;
if ($GET{'journal'} && $itemid)
{
my $journal = $GET{'journal'};
my $u = LJ::load_user($journal);
# sanity check
unless ($u) {
$body = $ML{'.error.usernotfound'};
return;
}
my $journalid = $u->{'userid'}+0;
$itemid = int($itemid / 256);
my $jumpid = 0;
$title = $ML{'.error.noentrytitle'};
if ($GET{'dir'} eq "next") {
$jumpid = LJ::get_itemid_after2($u, $itemid);
$body = $ML{'.error.noentry.next'};
} elsif ($GET{'dir'} eq "prev") {
$jumpid = LJ::get_itemid_before2($u, $itemid);
$body = $ML{'.error.noentry.prev'};
}
if ($jumpid) {
return BML::redirect(LJ::journal_base($u) . "/$jumpid.html");
}
}
return;
}
_code?><?page
title=><?_code return $title; _code?>
body=><?_code return $body; _code?>
page?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 799 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 907 B

BIN
livejournal/htdocs/img/btn_dn.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 598 B

Some files were not shown because too many files have changed in this diff Show More