875 lines
33 KiB
Perl
875 lines
33 KiB
Perl
# 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 = "<fieldset><legend>$name ";
|
|
$ret .=
|
|
"<span class='s2propgroup-outer-expandcollapse'> - <a href='' class='s2propgroup-expandcollapse' id='${propgroup}__expand'>"
|
|
. $class->ml('widget.s2propgroup.expand')
|
|
. "</a></span> ";
|
|
$ret .=
|
|
"<span class='s2propgroup-outer-expandcollapse'> - <a href='' class='s2propgroup-expandcollapse' id='${propgroup}__collapse'>"
|
|
. $class->ml('widget.s2propgroup.collapse')
|
|
. "</a></span>";
|
|
$ret .= "</legend></fieldset>";
|
|
|
|
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 .= "<p class='detail'>" . $class->ml('widget.s2propgroup.presentation.note') . "</p>";
|
|
|
|
$ret .=
|
|
"<div class='subheader subheader-presentation collapsible expanded' id='subheader__presentation__basic'><div class='collapse-button'>"
|
|
. $class->ml('collapsible.expanded')
|
|
. "</div> "
|
|
. $class->ml('widget.s2propgroup.presentation.basic')
|
|
. "</div>";
|
|
$ret .=
|
|
"<table summary='' cellspacing='0' class='prop-list first' id='proplist__presentation__basic'>";
|
|
$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 .= "</table>";
|
|
|
|
$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 .=
|
|
"<div class='subheader subheader-presentation collapsible expanded' id='subheader__presentation__additional'><div class='collapse-button'>"
|
|
. $class->ml('collapsible.expanded')
|
|
. "</div> "
|
|
. $class->ml('widget.s2propgroup.presentation.additional')
|
|
. "</div>";
|
|
$ret .=
|
|
"<table summary='' cellspacing='0' class='prop-list' id='proplist__presentation__additional'>";
|
|
}
|
|
$header_printed = 1;
|
|
$row_class = $count % 2 == 0 ? " even" : " odd";
|
|
$ret .= $class->output_prop( $props->{$prop_name}, $prop_name, $row_class, $u, $style,
|
|
$theme, $props );
|
|
$count++;
|
|
}
|
|
$ret .= "</table>" 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 .=
|
|
"<div class='subheader subheader-modules collapsible expanded' id='subheader__modules__${subheader}'><div class='collapse-button'>"
|
|
. $class->ml('collapsible.expanded')
|
|
. "</div> $subheaders{$subheader}</div>";
|
|
$ret .=
|
|
"<table summary='' cellspacing='0' class='prop-list$prop_list_class' id='proplist__modules__${subheader}'>";
|
|
$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, \%grouped_prop_override );
|
|
$count++;
|
|
}
|
|
$ret .= "</table>" 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 .=
|
|
"<div class='subheader subheader-$propgroup collapsible expanded' id='subheader__${propgroup}__${subheader}'><div class='collapse-button'>"
|
|
. $class->ml('collapsible.expanded')
|
|
. "</div>$subheaders{$subheader}</div>";
|
|
$ret .=
|
|
"<table summary='' cellspacing='0' class='prop-list$prop_list_class' id='proplist__${propgroup}__${subheader}'>";
|
|
$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 .= "</table>" 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 .=
|
|
"<div class='subheader subheader-$propgroup collapsible expanded' id='subheader__${propgroup}__${subheader}'><div class='collapse-button'>"
|
|
. $class->ml('collapsible.expanded')
|
|
. "</div>$subheaders{$subheader}</div>";
|
|
$ret .=
|
|
"<table summary='' cellspacing='0' class='prop-list$prop_list_class' id='proplist__${propgroup}__${subheader}'>";
|
|
$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++;
|
|
}
|
|
$ret .= "</table>" if $header_printed;
|
|
}
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
sub language_chooser {
|
|
my $class = shift;
|
|
my $u = shift;
|
|
|
|
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 @langs = LJ::S2::get_layout_langs( $pub, $style{'layout'} );
|
|
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'} );
|
|
|
|
# they have set a custom i18n layer
|
|
if ( $style{'i18n'}
|
|
&& ( $style{'i18nc'} != $style{'i18n'} || !defined $pub->{ $style{'i18n'} } ) )
|
|
{
|
|
push @langs, 'custom', $class->ml('widget.s2propgroup.language.custom');
|
|
$langcode = 'custom';
|
|
}
|
|
|
|
my $ret = "<tr class='prop-row' width='100%'>";
|
|
$ret .= "<td>" . $class->ml('widget.s2propgroup.language.label') . "</td><td>";
|
|
$ret .= $class->html_select(
|
|
{
|
|
name => "langcode",
|
|
selected => $langcode,
|
|
},
|
|
0 => $class->ml('widget.s2propgroup.language.default'),
|
|
@langs
|
|
) . "</td>";
|
|
$ret .=
|
|
"</tr><tr class='prop-row-note'><td colspan='100%' class='prop-note'>"
|
|
. $class->ml('widget.s2propgroup.language.note')
|
|
. "</td></tr>";
|
|
|
|
return $ret;
|
|
}
|
|
|
|
sub skip_prop {
|
|
my $class = shift;
|
|
my $prop = shift;
|
|
my $prop_name = shift;
|
|
my %opts = @_;
|
|
|
|
my $props_to_skip = $opts{props_to_skip};
|
|
my $theme = $opts{theme};
|
|
|
|
if ( !$prop ) {
|
|
return 1 unless $prop_name eq "linklist_support" && $theme && $theme->linklist_support_tab;
|
|
}
|
|
|
|
return 1 if $prop->{noui};
|
|
return 1 if $prop->{grouped};
|
|
|
|
return 1 if $props_to_skip && $props_to_skip->{$prop_name};
|
|
|
|
if ($theme) {
|
|
return 1 if $prop_name eq $theme->layout_prop;
|
|
return 1 if $prop_name eq $theme->show_sidebar_prop;
|
|
}
|
|
|
|
if ( $opts{user}->is_community ) {
|
|
return 1 if $prop_name eq "text_view_network";
|
|
return 1 if $prop_name eq "text_view_friends";
|
|
return 1 if $prop_name eq "text_view_friends_filter";
|
|
return 1 if $prop_name eq "module_subscriptionfilters_group";
|
|
}
|
|
else {
|
|
return 1 if $prop_name eq "text_view_friends_comm";
|
|
}
|
|
|
|
return 1 if $prop_name eq "custom_control_strip_colors";
|
|
return 1 if $prop_name eq "control_strip_bgcolor";
|
|
return 1 if $prop_name eq "control_strip_fgcolor";
|
|
return 1 if $prop_name eq "control_strip_bordercolor";
|
|
return 1 if $prop_name eq "control_strip_linkcolor";
|
|
|
|
my $hook_rv = LJ::Hooks::run_hook(
|
|
"skip_prop_override", $prop_name,
|
|
user => $opts{user},
|
|
theme => $theme,
|
|
style => $opts{style}
|
|
);
|
|
return $hook_rv if $hook_rv;
|
|
|
|
return 0;
|
|
}
|
|
|
|
sub output_prop {
|
|
my ( $class, $prop, $prop_name, $row_class, $u, $style, $theme, $props, $grouped_prop_override )
|
|
= @_;
|
|
|
|
# for themes that don't use the linklist_support prop
|
|
my $linklist_tab;
|
|
if ( !$prop && $prop_name eq "linklist_support" ) {
|
|
$linklist_tab = $theme->linklist_support_tab;
|
|
}
|
|
|
|
my $ret;
|
|
$ret .= "<tr class='prop-row$row_class' width='100%' valign='top'>";
|
|
|
|
if ($linklist_tab) {
|
|
$ret .=
|
|
"<td colspan='100%'>"
|
|
. $class->ml( 'widget.s2propgroup.linkslisttab', { 'name' => $linklist_tab } )
|
|
. "</td>";
|
|
$ret .= "</tr>";
|
|
return $ret;
|
|
}
|
|
|
|
$ret .=
|
|
"<td class='prop-header' valign='top'>"
|
|
. LJ::eall( $prop->{des} ) . " "
|
|
. LJ::help_icon("s2opt_$prop->{name}") . "</td>"
|
|
unless $prop->{type} eq "Color" || $prop->{type} eq "string[]";
|
|
|
|
$ret .= $class->output_prop_element( $prop, $prop_name, $u, $style, $theme, $props, 0,
|
|
$grouped_prop_override );
|
|
|
|
my $note = "";
|
|
$note .= LJ::eall( $prop->{note} ) if $prop->{note};
|
|
$ret .=
|
|
"</tr><tr class='prop-row-note$row_class'><td colspan='100%' class='prop-note'>$note</td>"
|
|
if $note;
|
|
|
|
$ret .= "</tr>";
|
|
return $ret;
|
|
}
|
|
|
|
sub output_prop_element {
|
|
my ( $class, $prop, $prop_name, $u, $style, $theme, $props, $is_group, $grouped_prop_override,
|
|
$overriding_values )
|
|
= @_;
|
|
$grouped_prop_override ||= {};
|
|
$overriding_values ||= {};
|
|
|
|
my $name = $prop->{name};
|
|
my $type = $prop->{type};
|
|
|
|
my $can_use = LJ::S2::can_use_prop( $u, $theme->layout_uniq, $name );
|
|
|
|
my %prop_values =
|
|
%$overriding_values
|
|
? %$overriding_values
|
|
: LJ::Customize->get_s2_prop_values( $name, $u, $style );
|
|
|
|
my $existing = $prop_values{existing};
|
|
my $override = $prop_values{override};
|
|
|
|
my %values = split( /\|/, $prop->{values} || '' );
|
|
my $existing_display =
|
|
defined $existing && defined $values{$existing} ? $values{$existing} : $existing;
|
|
|
|
$existing_display = LJ::eall($existing_display);
|
|
|
|
my $ret;
|
|
|
|
# visually grouped properties. Allow nesting to only two levels
|
|
if ( $type eq "string[]" && $is_group < 2 ) {
|
|
|
|
if ( $prop->{grouptype} eq "module" ) {
|
|
my $has_opts;
|
|
$ret .= "<td class='prop-grouped prop-$prop->{grouptype}' colspan=2>";
|
|
foreach my $prop_in_group (@$override) {
|
|
|
|
my $overriding_values;
|
|
if ( $grouped_prop_override->{$prop_in_group} ) {
|
|
$prop_in_group = $grouped_prop_override->{$prop_in_group};
|
|
$overriding_values = $grouped_prop_override->{"${prop_in_group}_values"};
|
|
}
|
|
|
|
if ( $prop_in_group =~ /opts_group$/ ) {
|
|
$has_opts = 1;
|
|
next;
|
|
}
|
|
$ret .= $class->output_prop_element(
|
|
$props->{$prop_in_group},
|
|
$prop_in_group, $u, $style, $theme, $props, $is_group + 1,
|
|
$grouped_prop_override, $overriding_values
|
|
);
|
|
}
|
|
|
|
my $modulename = $prop->{name};
|
|
$modulename =~ s/_group$//;
|
|
|
|
$ret .= "<label for='${modulename}_show'>" . LJ::eall( $prop->{des} ) . "</label>";
|
|
|
|
$ret .= $class->output_prop_element( $props->{"${modulename}_opts_group"},
|
|
"${modulename}_opts_group", $u, $style, $theme, $props, $is_group + 1 )
|
|
if $has_opts;
|
|
|
|
$ret .= "</td>";
|
|
}
|
|
elsif ( $prop->{grouptype} eq "moduleopts" ) {
|
|
$ret .= "<ul class='prop-moduleopts'>";
|
|
foreach my $prop_in_group (@$override) {
|
|
$ret .= "<li>"
|
|
. $class->output_prop_element( $props->{$prop_in_group},
|
|
$prop_in_group, $u, $style, $theme, $props, $is_group + 1 );
|
|
}
|
|
$ret .= "</ul>";
|
|
}
|
|
else {
|
|
$ret .=
|
|
"<td class='prop-grouped prop-$prop->{grouptype}' colspan=2><label class='prop-header'>"
|
|
. LJ::eall( $prop->{des} ) . " "
|
|
. LJ::help_icon("s2opt_$prop->{name}")
|
|
. "</label>";
|
|
|
|
foreach my $prop_in_group (@$override) {
|
|
$ret .= $class->output_prop_element( $props->{$prop_in_group},
|
|
$prop_in_group, $u, $style, $theme, $props, $is_group + 1 );
|
|
}
|
|
my $note = "";
|
|
$note .= LJ::eall( $prop->{note} ) if $prop->{note};
|
|
$ret .= "<ul class=''><li>$note</li></ul>" if $note;
|
|
$ret .= "</td>";
|
|
}
|
|
}
|
|
elsif ( $prop->{values} ) {
|
|
$ret .= "<td class='prop-input'>" 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 .= " <label>" . LJ::eall( $prop->{des} ) . "</label>" if $is_group && $prop->{des};
|
|
$ret .= "</td>" unless $is_group;
|
|
}
|
|
elsif ( $type eq "int" ) {
|
|
$ret .= "<td class='prop-input'>" unless $is_group;
|
|
$ret .= $class->html_text(
|
|
name => $name,
|
|
disabled => !$can_use,
|
|
value => $override,
|
|
maxlength => 5,
|
|
size => 7,
|
|
);
|
|
$ret .= " <label>" . LJ::eall( $prop->{des} ) . "</label>" if $is_group && $prop->{des};
|
|
$ret .= "</td>" unless $is_group;
|
|
}
|
|
elsif ( $type eq "bool" ) {
|
|
$ret .= "<td class='prop-check'>" 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 .= "</td>" unless $is_group;
|
|
}
|
|
elsif ( $type eq "string" ) {
|
|
my $rows = $prop->{rows} ? $prop->{rows} + 0 : 0;
|
|
my $cols = $prop->{cols} ? $prop->{cols} + 0 : 0;
|
|
my $full = $prop->{full} ? $prop->{full} + 0 : 0;
|
|
|
|
$ret .= "<td class='prop-input'>" unless $is_group;
|
|
if ( $full > 0 ) {
|
|
$ret .= $class->html_textarea(
|
|
name => $name,
|
|
disabled => !$can_use,
|
|
value => $override,
|
|
rows => "40",
|
|
cols => "40",
|
|
style => "width: 97%; height: 350px; ",
|
|
);
|
|
}
|
|
elsif ( $rows > 0 && $cols > 0 ) {
|
|
$ret .= $class->html_textarea(
|
|
name => $name,
|
|
disabled => !$can_use,
|
|
value => $override,
|
|
rows => $rows,
|
|
cols => $cols,
|
|
);
|
|
}
|
|
else {
|
|
my ( $size, $maxlength ) = ( $prop->{size} || 30, $prop->{maxlength} || 255 );
|
|
|
|
$ret .= $class->html_text(
|
|
name => $name,
|
|
disabled => !$can_use,
|
|
value => $override,
|
|
maxlength => $maxlength,
|
|
size => $size,
|
|
);
|
|
}
|
|
$ret .= "</td>" unless $is_group;
|
|
}
|
|
elsif ( $type eq "Color" ) {
|
|
$ret .= "<td class='prop-color'>" unless $is_group;
|
|
$ret .= $class->html_color(
|
|
name => $name,
|
|
disabled => !$can_use,
|
|
default => $override,
|
|
des => $prop->{des},
|
|
onchange => "Customize.CustomizeTheme.form_change();",
|
|
no_btn => 1,
|
|
);
|
|
$ret .= "</td>" unless $is_group;
|
|
$ret .= "<td>" . LJ::eall( $prop->{des} ) . " " . LJ::help_icon("s2opt_$name") . "</td>";
|
|
}
|
|
|
|
my $offhelp = !$can_use ? LJ::help_icon( 's2propoff', ' ' ) : "";
|
|
$ret .= " $offhelp";
|
|
|
|
return $ret;
|
|
}
|
|
|
|
sub handle_post {
|
|
my $class = shift;
|
|
my $post = shift;
|
|
my %opts = @_;
|
|
|
|
my $u = $class->get_effective_remote();
|
|
die "Invalid user." unless $u;
|
|
|
|
my $style = LJ::S2::load_style( $u->prop('s2_style') );
|
|
die "Style not found." unless $style && $style->{userid} == $u->id;
|
|
|
|
my $post_fields_of_parent = LJ::Widget->post_fields_of_widget("CustomizeTheme");
|
|
if ( $post_fields_of_parent->{reset} ) {
|
|
|
|
# reset all props except the layout props
|
|
my $current_theme = LJ::Customize->get_current_theme($u);
|
|
my $layout_prop = $current_theme->layout_prop;
|
|
my $show_sidebar_prop = $current_theme->show_sidebar_prop;
|
|
|
|
my %override = %$post;
|
|
delete $override{$layout_prop};
|
|
delete $override{$show_sidebar_prop};
|
|
|
|
LJ::Customize->save_s2_props( $u, $style, \%override, reset => 1 );
|
|
LJ::Customize->save_language( $u, $post->{langcode}, reset => 1 )
|
|
if defined $post->{langcode};
|
|
}
|
|
else {
|
|
my %override = map { $_ => "" } keys %$post;
|
|
|
|
# ignore all values after the first true $value
|
|
# only checkboxes have multiple values (forced post of 0,
|
|
# so we don't ignore checkboxes that the user just unchecked)
|
|
foreach my $key ( keys %$post ) {
|
|
foreach my $value ( split( /\0/, $post->{$key} ) ) {
|
|
$override{$key} ||= $value;
|
|
}
|
|
}
|
|
|
|
LJ::Customize->save_s2_props( $u, $style, \%override );
|
|
LJ::Customize->save_language( $u, $post->{langcode} ) if defined $post->{langcode};
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
# return if the propgroup has props to display or not
|
|
sub group_exists_with_props {
|
|
my $class = shift;
|
|
my %opts = @_;
|
|
|
|
my $u = $opts{user};
|
|
my $props = $opts{props};
|
|
my $groupprops = $opts{groupprops};
|
|
|
|
my $theme = LJ::Customize->get_current_theme($u);
|
|
foreach my $prop_name (@$groupprops) {
|
|
return 1
|
|
unless $class->skip_prop(
|
|
$props->{$prop_name}, $prop_name,
|
|
theme => $theme,
|
|
user => $u
|
|
);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
sub js {
|
|
my $collapsed = LJ::ejs_string( LJ::Lang::ml('collapsible.collapsed') );
|
|
my $expanded = LJ::ejs_string( LJ::Lang::ml('collapsible.expanded') );
|
|
|
|
qq [
|
|
ml: {
|
|
collapsed: $collapsed,
|
|
expanded: $expanded
|
|
},
|
|
]
|
|
. q [
|
|
initWidget: function () {
|
|
var self = this;
|
|
|
|
// add event listeners to all of the subheaders
|
|
var subheaders = DOM.getElementsByClassName(document, "subheader");
|
|
subheaders.forEach(function (subheader) {
|
|
DOM.addEventListener(subheader, "click", function (evt) { self.alterSubheader(subheader.id) });
|
|
});
|
|
|
|
// show the expand/collapse links
|
|
var ec_spans = DOM.getElementsByClassName(document, "s2propgroup-outer-expandcollapse");
|
|
ec_spans.forEach(function (ec_span) {
|
|
ec_span.style.display = "inline";
|
|
});
|
|
|
|
// add event listeners to all of the expand/collapse links
|
|
var ec_links = DOM.getElementsByClassName(document, "s2propgroup-expandcollapse");
|
|
ec_links.forEach(function (ec_link) {
|
|
DOM.addEventListener(ec_link, "click", function (evt) { self.expandCollapseAll(evt, ec_link.id) });
|
|
});
|
|
},
|
|
alterSubheader: function (subheaderid, override) {
|
|
var self = this;
|
|
var proplistid = subheaderid.replace(/subheader/, 'proplist');
|
|
|
|
// figure out whether to expand or collapse
|
|
var expand = !DOM.hasClassName($(subheaderid), 'expanded');
|
|
if (override) {
|
|
if (override == "expand") {
|
|
expand = 1;
|
|
} else {
|
|
expand = 0;
|
|
}
|
|
}
|
|
|
|
if (expand) {
|
|
// expand
|
|
DOM.removeClassName($(subheaderid), 'collapsed');
|
|
DOM.addClassName($(subheaderid), 'expanded');
|
|
|
|
DOM.getElementsByClassName($(subheaderid), 'collapse-button')
|
|
.forEach( function(button) {
|
|
button.innerText = self.ml.expanded;
|
|
} );
|
|
|
|
$(proplistid).style.display = "block";
|
|
} else {
|
|
// collapse
|
|
DOM.removeClassName($(subheaderid), 'expanded');
|
|
DOM.addClassName($(subheaderid), 'collapsed');
|
|
|
|
DOM.getElementsByClassName($(subheaderid), 'collapse-button')
|
|
.forEach( function(button) {
|
|
button.innerText = self.ml.collapsed;
|
|
} );
|
|
|
|
$(proplistid).style.display = "none";
|
|
}
|
|
},
|
|
expandCollapseAll: function (evt, ec_linkid) {
|
|
var self = this;
|
|
var action = ec_linkid.replace(/.+__(.+)/, '$1');
|
|
var propgroup = ec_linkid.replace(/(.+)__.+/, '$1');
|
|
|
|
var propgroupSubheaders = DOM.getElementsByClassName(document, "subheader-" + propgroup);
|
|
propgroupSubheaders.forEach(function (subheader) {
|
|
self.alterSubheader(subheader.id, action);
|
|
});
|
|
Event.stop(evt);
|
|
},
|
|
onRefresh: function (data) {
|
|
this.initWidget();
|
|
}
|
|
];
|
|
}
|
|
|
|
1;
|