255 lines
7 KiB
Perl
255 lines
7 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::Setting;
|
||
|
|
use strict;
|
||
|
|
use warnings;
|
||
|
|
use Carp qw(croak);
|
||
|
|
use LJ::ModuleLoader;
|
||
|
|
|
||
|
|
# require all settings
|
||
|
|
LJ::ModuleLoader->require_subclasses("LJ::Setting");
|
||
|
|
LJ::ModuleLoader->require_subclasses("DW::Setting");
|
||
|
|
|
||
|
|
# ----------------------------------------------------------------------------
|
||
|
|
|
||
|
|
sub should_render { 1 }
|
||
|
|
sub is_conditional_setting { 0 }
|
||
|
|
sub disabled { 0 }
|
||
|
|
sub selected { 0 }
|
||
|
|
sub label { "" }
|
||
|
|
sub actionlink { "" }
|
||
|
|
sub helpurl { "" }
|
||
|
|
sub option { "" }
|
||
|
|
sub htmlcontrol { "" }
|
||
|
|
sub htmlcontrol_label { "" }
|
||
|
|
|
||
|
|
sub error_check {
|
||
|
|
my ( $class, $u, $args ) = @_;
|
||
|
|
my $val = $class->get_arg( $args, "foo" );
|
||
|
|
|
||
|
|
#unless ($val =~ /blah/) {
|
||
|
|
# $class->errors("foo" => "Invalid foo");
|
||
|
|
#}
|
||
|
|
|
||
|
|
die "No 'error_check' configured for settings module '$class'\n";
|
||
|
|
}
|
||
|
|
|
||
|
|
sub as_html {
|
||
|
|
my ( $class, $u, $errmap ) = @_;
|
||
|
|
return "No 'as_html' implemented for $class.";
|
||
|
|
}
|
||
|
|
|
||
|
|
sub save {
|
||
|
|
my ( $class, $u, $postargs, @classes ) = @_;
|
||
|
|
if ( $class ne __PACKAGE__ ) {
|
||
|
|
die "No 'save' implemented for '$class'\n";
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
die "No classes given to save\n" unless @classes;
|
||
|
|
}
|
||
|
|
|
||
|
|
my %posted; # class -> key -> value
|
||
|
|
while ( my ( $k, $v ) = each %$postargs ) {
|
||
|
|
my ( $class, $key ) = class_from_key($k);
|
||
|
|
$posted{$class}{$key} = $v;
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach my $setclass (@classes) {
|
||
|
|
my $args = $posted{$setclass} || {};
|
||
|
|
$setclass->save( $u, $args );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
# ----------------------------------------------------------------------------
|
||
|
|
|
||
|
|
# Don't override:
|
||
|
|
|
||
|
|
# Internal method to do *proper* argument -> class/key mapping.
|
||
|
|
sub class_from_key {
|
||
|
|
my ($val) = @_;
|
||
|
|
|
||
|
|
my ( $class, $key ) = $val =~ /^((?:[a-zA-Z0-9]+__)+[a-zA-Z0-9]+)_([\w\[\]]+)$/;
|
||
|
|
$class =~ s/__/::/g if $class;
|
||
|
|
|
||
|
|
return ( $class, $key );
|
||
|
|
}
|
||
|
|
|
||
|
|
sub pkgkey {
|
||
|
|
my $class = shift;
|
||
|
|
$class =~ s/::/__/g;
|
||
|
|
return $class . "_";
|
||
|
|
}
|
||
|
|
|
||
|
|
sub errdiv {
|
||
|
|
my ( $class, $errs, $key ) = @_;
|
||
|
|
return "" unless $errs;
|
||
|
|
|
||
|
|
# $errs can be a hashref of { $class => LJ::Error::SettingSave::Foo } or a map of
|
||
|
|
# { $errfield => $errtxt }. this converts the former to latter.
|
||
|
|
if ( my $classerr = $errs->{$class} ) {
|
||
|
|
$errs = $classerr->field('map');
|
||
|
|
}
|
||
|
|
|
||
|
|
my $err = $errs->{$key} or return "";
|
||
|
|
|
||
|
|
# TODO: red is temporary. move to css.
|
||
|
|
return "<div style='color: red' class='ljinlinesettingerror'>$err</div>";
|
||
|
|
}
|
||
|
|
|
||
|
|
# don't override this.
|
||
|
|
sub errors {
|
||
|
|
my ( $class, %map ) = @_;
|
||
|
|
|
||
|
|
my $errclass = $class;
|
||
|
|
$errclass =~ s/^([a-zA-Z0-9]+)::Setting:://;
|
||
|
|
$errclass = "$1::Error::SettingSave::" . $errclass;
|
||
|
|
eval "\@${errclass}::ISA = ( 'LJ::Error::SettingSave' );";
|
||
|
|
|
||
|
|
my $eo = eval { $errclass->new( map => \%map ) };
|
||
|
|
$eo->throw;
|
||
|
|
}
|
||
|
|
|
||
|
|
# gets a key out of the $args hash, which can be either \%POST or a class-specific one
|
||
|
|
sub get_arg {
|
||
|
|
my ( $class, $args, $which ) = @_;
|
||
|
|
my $key = $class->pkgkey;
|
||
|
|
return $args->{"${key}$which"} || $args->{$which} || "";
|
||
|
|
}
|
||
|
|
|
||
|
|
# called like:
|
||
|
|
# LJ::Setting->error_map($u, \%POST, @multiple_setting_classnames)
|
||
|
|
# or:
|
||
|
|
# LJ::Setting::SpecificOption->error_map($u, \%POST)
|
||
|
|
# returns:
|
||
|
|
# undef if no errors found,
|
||
|
|
# LJ::SettingErrors object if any errors.
|
||
|
|
sub error_map {
|
||
|
|
my ( $class, $u, $post, @classes ) = @_;
|
||
|
|
if ( $class ne __PACKAGE__ ) {
|
||
|
|
croak("Can't call error_map on LJ::Setting subclass with \@classes set.") if @classes;
|
||
|
|
@classes = ($class);
|
||
|
|
}
|
||
|
|
|
||
|
|
my %errors;
|
||
|
|
foreach my $setclass (@classes) {
|
||
|
|
my $okay = eval { $setclass->error_check( $u, $post ); };
|
||
|
|
next if $okay;
|
||
|
|
$errors{$setclass} = $@;
|
||
|
|
}
|
||
|
|
return undef unless %errors;
|
||
|
|
return \%errors;
|
||
|
|
}
|
||
|
|
|
||
|
|
# save all of the settings that were changed
|
||
|
|
# $u: user whose settings we're changing
|
||
|
|
# $post: reference to %POST hash
|
||
|
|
# $all_settings: reference to array of all settings that are on this page
|
||
|
|
# returns any errors and the post args for each setting
|
||
|
|
sub save_all {
|
||
|
|
shift if $_[0] eq __PACKAGE__;
|
||
|
|
my ( $u, $post, $all_settings ) = @_;
|
||
|
|
my %posted; # class -> key -> value
|
||
|
|
my %returns;
|
||
|
|
|
||
|
|
while ( my ( $k, $v ) = each %$post ) {
|
||
|
|
my ( $class, $key ) = class_from_key($k);
|
||
|
|
next unless $class;
|
||
|
|
$posted{$class}{$key} = $v;
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach my $class (@$all_settings) {
|
||
|
|
my $post_args = $posted{$class};
|
||
|
|
$post_args ||= {};
|
||
|
|
my $save_errors;
|
||
|
|
if ($post_args) {
|
||
|
|
my $sv = eval { $class->save( $u, $post_args ); };
|
||
|
|
if ( my $err = $@ ) {
|
||
|
|
$save_errors = $err->field('map') if ref $err;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
$returns{$class}{save_errors} = $save_errors;
|
||
|
|
$returns{$class}{post_args} = $post_args;
|
||
|
|
}
|
||
|
|
|
||
|
|
return \%returns;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub save_had_errors {
|
||
|
|
my $class = shift;
|
||
|
|
my $save_rv = shift;
|
||
|
|
return 0 unless ref $save_rv;
|
||
|
|
|
||
|
|
my @settings = @_; # optional, for specific settings
|
||
|
|
@settings = keys %$save_rv unless @settings;
|
||
|
|
|
||
|
|
foreach my $setting (@settings) {
|
||
|
|
my $errors = $save_rv->{$setting}->{save_errors} || {};
|
||
|
|
return 1 if %$errors;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
sub errors_from_save {
|
||
|
|
my $class = shift;
|
||
|
|
my $save_rv = shift;
|
||
|
|
|
||
|
|
return $save_rv->{$class}->{save_errors};
|
||
|
|
}
|
||
|
|
|
||
|
|
sub args_from_save {
|
||
|
|
my $class = shift;
|
||
|
|
my $save_rv = shift;
|
||
|
|
|
||
|
|
return $save_rv->{$class}->{post_args};
|
||
|
|
}
|
||
|
|
|
||
|
|
sub ml {
|
||
|
|
my ( $class, $code, $vars ) = @_;
|
||
|
|
|
||
|
|
# can pass in a string and check 2 places in order:
|
||
|
|
# 1) setting.foo.text => general .setting.foo.text (overridden by current page)
|
||
|
|
# 2) setting.foo.text => general setting.foo.text (defined in en(_LJ).dat)
|
||
|
|
|
||
|
|
# whether passed with or without a ".", eat that immediately
|
||
|
|
$code =~ s/^\.//;
|
||
|
|
|
||
|
|
# 1) try with a ., for current page override in 'general' domain
|
||
|
|
# 2) try without a ., for global version in 'general' domain
|
||
|
|
foreach my $curr_code ( ".$code", $code ) {
|
||
|
|
my $string = LJ::Lang::ml( $curr_code, $vars );
|
||
|
|
return "" if $string eq "_none";
|
||
|
|
return $string unless LJ::Lang::is_missing_string($string);
|
||
|
|
}
|
||
|
|
|
||
|
|
# return the class name if we didn't find anything
|
||
|
|
$class =~ /.+::(\w+)$/;
|
||
|
|
return $1;
|
||
|
|
}
|
||
|
|
|
||
|
|
package LJ::Error::SettingSave;
|
||
|
|
use base 'LJ::Error';
|
||
|
|
|
||
|
|
sub user_caused { 1 }
|
||
|
|
sub fields { qw(map); } # key -> english (keys are LJ::Setting:: subclass-defined)
|
||
|
|
|
||
|
|
sub as_string {
|
||
|
|
my $self = shift;
|
||
|
|
my $map = $self->field('map');
|
||
|
|
return join( ", ", map { $_ . '=' . $map->{$_} } sort keys %$map );
|
||
|
|
}
|
||
|
|
|
||
|
|
1;
|