ljr/livejournal/htdocs/customize/layer.bml

439 lines
15 KiB
Plaintext
Raw Normal View History

2019-02-05 21:49:12 +00:00
<?_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?>