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,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?>