# This code was forked from the LiveJournal project owned and operated
# by Live Journal, Inc. The code has been modified and expanded by
# Dreamwidth Studios, LLC. These files were originally licensed under
# the terms of the license supplied by Live Journal, Inc, which can
# currently be found at:
#
# http://code.livejournal.org/trac/livejournal/browser/trunk/LICENSE-LiveJournal.txt
#
# In accordance with the original license, this code and all its
# modifications are provided under the GNU General Public License.
# A copy of that license can be found in the LICENSE file included as
# part of this distribution.
package LJ::Widget::S2PropGroup;
use strict;
use base qw(LJ::Widget);
use Carp qw(croak);
use LJ::Customize;
use List::Util qw( first );
sub authas { 1 }
sub need_res {
qw( stc/coloris.css js/vendor/coloris.js stc/collapsible.css stc/collapsible.css js/vendor/codemirror/codemirror.js js/vendor/codemirror/modes/css.js stc/cssedit/codemirror.css stc/cssedit/twilight.css stc/cssedit/show-hint.js stc/cssedit/show-hint.css stc/cssedit/css-hint.js );
}
sub need_res_opts { ( priority => $LJ::OLD_RES_PRIORITY ) }
sub render_body {
my $class = shift;
my %opts = @_;
my $u = $class->get_effective_remote();
die "Invalid user." unless LJ::isu($u);
my $props = $opts{props};
my $propgroup = $opts{propgroup};
my $groupprops = $opts{groupprops};
return "" unless ( $props && $propgroup && $groupprops ) || $opts{show_lang_chooser};
my $style = LJ::S2::load_style( $u->prop('s2_style') );
die "Style not found." unless $style && $style->{userid} == $u->id;
my $name = LJ::Customize->propgroup_name( $propgroup, $u, $style );
my $ret = "
";
my $theme = LJ::Customize->get_current_theme($u);
my $row_class = "";
my $count = 1;
if ( $propgroup eq "presentation" ) {
my @basic_props = $theme->display_option_props;
my %is_basic_prop = map { $_ => 1 } @basic_props;
$ret .= "
";
$ret .= $class->language_chooser($u) if $opts{show_lang_chooser};
foreach my $prop_name (@basic_props) {
next
if $class->skip_prop(
$props->{$prop_name}, $prop_name,
theme => $theme,
user => $u
);
if ( $opts{show_lang_chooser} ) {
# start on gray, since the language chooser will be white
$row_class = $count % 2 != 0 ? " odd" : " even";
}
else {
$row_class = $count % 2 == 0 ? " even" : " odd";
}
$ret .= $class->output_prop( $props->{$prop_name}, $prop_name, $row_class, $u, $style,
$theme, $props );
$count++;
}
$ret .= "
";
$count = 1; # reset counter
my $header_printed = 0;
foreach my $prop_name (@$groupprops) {
next
if $class->skip_prop(
$props->{$prop_name}, $prop_name,
props_to_skip => \%is_basic_prop,
theme => $theme,
user => $u
);
# need to print the header inside the foreach because we don't want it printed if
# there's no props in this group that are also in this subheader
unless ($header_printed) {
$ret .=
"
" if $header_printed;
}
elsif ( $propgroup eq "modules" ) {
my %prop_values = LJ::Customize->get_s2_prop_values( "module_layout_sections", $u, $style );
my $layout_sections_values = $prop_values{override};
my @layout_sections_order = split( /\|/, $layout_sections_values );
# allow to override the default property with your own custom property definition. Created and values set in layout layers.
my %grouped_prop_override =
LJ::Customize->get_s2_prop_values( "grouped_property_override", $u, $style, noui => 1 );
%grouped_prop_override = %{ $grouped_prop_override{override} }
if %{ $grouped_prop_override{override} || {} };
my %subheaders = @layout_sections_order;
$subheaders{none} = "Unassigned";
# use the module section order as defined by the layout
my $i = 0;
@layout_sections_order = grep { $i++ % 2 == 0; } @layout_sections_order;
my %prop_in_subheader;
foreach my $prop_name (@$groupprops) {
next unless $prop_name =~ /_group$/;
# use module_*_section for the dropdown
my $prop_name_section = $prop_name;
$prop_name_section =~ s/(.*)_group$/$1_section/;
my $overriding_prop_name = $grouped_prop_override{$prop_name_section};
# module_*_section_override overrides module_*_section;
# for use in child layouts since they cannot redefine an existing property
my $prop_name_section_override =
defined $overriding_prop_name ? $props->{$overriding_prop_name}->{values} : undef;
# put this property under the proper subheader (this is the original; may be overriden)
my %prop_values = LJ::Customize->get_s2_prop_values( $prop_name_section, $u, $style );
if ($prop_name_section_override) {
$prop_name_section = $overriding_prop_name;
# check if we have anything previously saved into the overriding property. If we don't we retain
# the value of the original (non-overridden) property, so we don't break existing customizations
my %overriding_prop_values =
LJ::Customize->get_s2_prop_values( $prop_name_section, $u, $style );
my $contains_values = 0;
foreach ( keys %overriding_prop_values ) {
if ( defined $overriding_prop_values{$_} ) {
$contains_values++;
last;
}
}
%prop_values = %overriding_prop_values if $contains_values;
$grouped_prop_override{"${prop_name_section}_values"} = \%prop_values;
}
# populate section dropdown values with the layout's list of available sections, if not already set
$props->{$prop_name_section}->{values} ||= $layout_sections_values;
if ($prop_name_section_override) {
my %override_sections = split( /\|/, $prop_name_section_override );
while ( my ( $key, $value ) = each %override_sections ) {
unless ( $subheaders{$key} ) {
$subheaders{$key} = $value;
push @layout_sections_order, $key;
}
}
}
# see whether a cap is needed for this module and don't show the module if the user does not have that cap
my $cap;
$cap = $props->{$prop_name}->{requires_cap};
next if $cap && !( $u->get_cap($cap) );
# force it to the "none" section, if property value is not a valid subheader
my $subheader = $subheaders{ $prop_values{override} } ? $prop_values{override} : "none";
$prop_in_subheader{$subheader} ||= [];
push @{ $prop_in_subheader{$subheader} }, $prop_name;
}
my $subheader_counter = 1;
foreach my $subheader (@layout_sections_order) {
my $header_printed = 0;
foreach my $prop_name ( @{ $prop_in_subheader{$subheader} } ) {
next
if $class->skip_prop(
$props->{$prop_name}, $prop_name,
theme => $theme,
user => $u,
style => $style
);
unless ($header_printed) {
my $prop_list_class = '';
$prop_list_class = " first" if $subheader_counter == 1;
$ret .=
"
" if $header_printed;
}
}
elsif ( $propgroup eq "text" ) {
my %subheaders = LJ::Customize->get_propgroup_subheaders;
# props under the unsorted subheader include all props in the group that aren't under any of the other subheaders
my %unsorted_props = map { $_ => 1 } @$groupprops;
foreach my $subheader ( keys %subheaders ) {
my @subheader_props = eval "\$theme->${subheader}_props";
foreach my $prop_name (@subheader_props) {
delete $unsorted_props{$prop_name} if $unsorted_props{$prop_name};
}
}
my $subheader_counter = 1;
foreach my $subheader ( LJ::Customize->get_propgroup_subheaders_order ) {
my $header_printed = 0;
my @subheader_props;
if ( $subheader eq "unsorted" ) {
@subheader_props = keys %unsorted_props;
}
else {
@subheader_props = eval "\$theme->${subheader}_props";
}
next unless @subheader_props;
my %prop_is_in_subheader = map { $_ => 1 } @subheader_props;
foreach my $prop_name (@$groupprops) {
next
if $class->skip_prop(
$props->{$prop_name}, $prop_name,
theme => $theme,
user => $u,
style => $style
);
next unless $prop_is_in_subheader{$prop_name};
# need to print the header inside the foreach because we don't want it printed if
# there's no props in this group that are also in this subheader
unless ($header_printed) {
my $prop_list_class = "";
$prop_list_class = " first" if $subheader_counter == 1;
$ret .=
"
"
. $class->ml('collapsible.expanded')
. "
$subheaders{$subheader}
";
$ret .=
"
";
$header_printed = 1;
$subheader_counter++;
$count = 1; # reset counter
}
$row_class = $count % 2 == 0 ? " even" : " odd";
$ret .=
$class->output_prop( $props->{$prop_name}, $prop_name, $row_class, $u, $style,
$theme, $props );
$count++;
}
#If we're in the module subsection, we also need to render the Custom Text widget
if ( $subheaders{$subheader} eq $class->ml('customize.propgroup_subheaders.module') ) {
$ret .= LJ::Widget::CustomTextModule->render( count => $count );
}
$ret .= "
" if $header_printed;
}
}
else {
my %subheaders = LJ::Customize->get_propgroup_subheaders;
# props under the unsorted subheader include all props in the group that aren't under any of the other subheaders
my %unsorted_props = map { $_ => 1 } @$groupprops;
foreach my $subheader ( keys %subheaders ) {
my @subheader_props = eval "\$theme->${subheader}_props";
foreach my $prop_name (@subheader_props) {
delete $unsorted_props{$prop_name} if $unsorted_props{$prop_name};
}
}
my $subheader_counter = 1;
foreach my $subheader ( LJ::Customize->get_propgroup_subheaders_order ) {
my $header_printed = 0;
my @subheader_props;
if ( $subheader eq "unsorted" ) {
@subheader_props = keys %unsorted_props;
}
else {
@subheader_props = eval "\$theme->${subheader}_props";
}
next unless @subheader_props;
my %prop_is_in_subheader = map { $_ => 1 } @subheader_props;
foreach my $prop_name (@$groupprops) {
next
if $class->skip_prop(
$props->{$prop_name}, $prop_name,
theme => $theme,
user => $u,
style => $style
);
next unless $prop_is_in_subheader{$prop_name};
# need to print the header inside the foreach because we don't want it printed if
# there's no props in this group that are also in this subheader
unless ($header_printed) {
my $prop_list_class = "";
$prop_list_class = " first" if $subheader_counter == 1;
$ret .=
"
" unless $is_group;
# take the list of allowed values, determine whether we allow custom values
# and whether we have a value not in the list (possibly set through the layer editor)
# if so, prepend custom values
my @values = split( /\|/, $prop->{values} );
unshift @values, $override, "Custom: $override"
if $prop->{allow_other} && defined $override && !first { $_ eq $override } @values;
$ret .= $class->html_select(
{
name => $name,
disabled => !$can_use,
selected => $override,
},
@values,
);
$ret .= " " if $is_group && $prop->{des};
$ret .= "
" unless $is_group;
unless ( $prop->{obsolete} ) { # can't be changed, so don't print
$ret .= $class->html_check(
name => $name,
disabled => !$can_use,
selected => $override,
label => $prop->{label},
id => $name,
);
# force the checkbox to be submitted, if the user unchecked it
# so that it can be processed (disabled) when handling the post
$ret .= $class->html_hidden( "${name}", "0", { disabled => !$can_use } );
}
$ret .= "