LJSUP-17669: Login.bml form refactoring
[livejournal.git] / cgi-bin / LJ / Setting.pm
blob8abb5bbad84058dfe89ee7d810b17ac7c6e196d4
1 package LJ::Setting;
2 use strict;
3 use warnings;
4 use Carp qw(croak);
5 use LJ::ModuleLoader;
7 # Autouse all settings
8 LJ::ModuleLoader->autouse_subclasses("LJ::Setting");
9 use LJ::Setting::WebmasterTools;
10 use LJ::Setting::WebmasterTools::Yandex;
12 # ----------------------------------------------------------------------------
14 my $is_savebutton_hide = 0;
15 my $veiwonly_mode = 0;
17 sub should_render { 1 }
18 sub disabled { 0 }
19 sub selected { 0 }
20 sub tags { () }
21 sub label { "" }
22 sub actionlink { "" }
23 sub helpurl { "" }
24 sub option { "" }
25 sub htmlcontrol { "" }
26 sub htmlcontrol_label { "" }
27 sub raw_html { "" }
28 sub is_savebutton_hide { return $is_savebutton_hide; }
29 sub hide_savebutton { $is_savebutton_hide = 1; }
30 sub show_savebutton { $is_savebutton_hide = 0; }
31 sub is_viewonly_mode { return $veiwonly_mode; }
32 sub start_viewonly {
33 LJ::need_var(no_submit_window => 1);
34 $veiwonly_mode = 1;
35 hide_savebutton();
37 sub cancel_viewonly {
38 $veiwonly_mode = 0;
39 show_savebutton();
43 sub handle_post {
44 my ( $class, $params, $widget ) = @_;
45 delete @$params{qw/lj_form_auth _widget_id _widget_ippu _widget_class/};
47 my %res;
49 eval {
50 %res = $widget->save(LJ::get_remote(), $params);
53 return %res;
56 sub form_start {
57 my $self = shift;
59 return '<form action="javascript: return false" id="settingwindow_form">'
60 . '<input type="hidden" name="_widget_class" value="'
61 . ( ref($self) || $self )
62 . '">'
63 # hidden for posting submit button (form can have several submit buttons)
64 . '<input type="hidden" id="form_submit" name="submit" value="">' ;
67 sub form_end {
68 return '</form>';
71 sub form_auth {
72 return LJ::form_auth();
75 sub error_check {
76 my ($class, $u, $args) = @_;
77 my $val = $class->get_arg($args, "foo");
79 die "No 'error_check' configured for settings module '$class'\n";
82 sub as_html {
83 my ($class, $u, $errmap) = @_;
84 return "No 'as_html' implemented for $class.";
87 sub save {
88 my ($class, $u, $postargs, @classes) = @_;
89 if ($class ne __PACKAGE__) {
90 die "No 'save' implemented for '$class'\n";
91 } else {
92 die "No classes given to save\n" unless @classes;
95 my %posted; # class -> key -> value
96 while (my ($k, $v) = each %$postargs) {
97 next unless $k =~ /^LJ__Setting__([a-zA-Z0-9]+)_(\w+)$/;
98 my $class = "LJ::Setting::$1";
99 my $key = $2;
100 $posted{$class}{$key} = $v;
103 foreach my $setclass (@classes) {
104 my $args = $posted{$setclass} || {};
105 $setclass->save($u, $args);
109 # ----------------------------------------------------------------------------
111 # Don't override:
113 sub pkgkey {
114 my $class = shift;
115 $class =~ s/::/__/g;
116 return $class . "_";
119 sub errdiv {
120 my ($class, $errs, $key) = @_;
121 return "" unless $errs;
123 # $errs can be a hashref of { $class => LJ::Error::SettingSave::Foo } or a map of
124 # { $errfield => $errtxt }. this converts the former to latter.
125 if (my $classerr = $errs->{$class}) {
126 $errs = $classerr->field('map');
129 my $err = $errs->{$key} or return "";
130 # TODO: red is temporary. move to css.
131 return "<div style='color: red' class='ljinlinesettingerror'>$err</div>";
134 # don't override this.
135 sub errors {
136 my ($class, %map) = @_;
138 my $errclass = $class;
139 $errclass =~ s/^LJ::Setting:://;
140 $errclass = "LJ::Error::SettingSave::" . $errclass;
142 eval {
143 no strict 'refs';
144 @{"${errclass}::ISA"} = ('LJ::Error::SettingSave');
147 warn $@ if $@;
149 my $eo = eval { $errclass->new(map => \%map) };
150 $eo->log;
151 $eo->throw;
154 # gets a key out of the $args hash, which can be either \%POST or a class-specific one
155 sub get_arg {
156 my ($class, $args, $which) = @_;
157 my $key = $class->pkgkey;
158 return $args->{"${key}$which"} || $args->{$which} || "";
161 # called like:
162 # LJ::Setting->error_map($u, \%POST, @multiple_setting_classnames)
163 # or:
164 # LJ::Setting::SpecificOption->error_map($u, \%POST)
165 # returns:
166 # undef if no errors found,
167 # LJ::SettingErrors object if any errors.
168 sub error_map {
169 my ($class, $u, $post, @classes) = @_;
170 if ($class ne __PACKAGE__) {
171 croak("Can't call error_map on LJ::Setting subclass with \@classes set.") if @classes;
172 @classes = ($class);
175 my %errors;
176 foreach my $setclass (@classes) {
177 my $okay = eval {
178 $setclass->error_check($u, $post);
180 next if $okay;
181 $errors{$setclass} = $@;
183 return undef unless %errors;
184 return \%errors;
187 # save all of the settings that were changed
188 # $u: user whose settings we're changing
189 # $post: reference to %POST hash
190 # $all_settings: reference to array of all settings that are on this page
191 # returns any errors and the post args for each setting
192 sub save_all {
193 shift if $_[0] eq __PACKAGE__;
194 my ($u, $post, $all_settings) = @_;
195 my %posted; # class -> key -> value
196 my %returns;
198 while (my ($k, $v) = each %$post) {
199 # matches both:
200 # LJ__Setting__Example_field (for LJ::Setting::Example)
201 # LJ__Setting__Example__Example2_field (for LJ::Setting::Example::Example2)
202 next unless $k =~ /^LJ__Setting__([a-zA-Z0-9]+)(__[a-zA-Z0-9]+)?_(\w+)$/;
203 my $class = "LJ::Setting::$1";
204 $class .= $2 if defined $2;
205 my $key = $3;
206 $class =~ s/__/::/g;
207 $posted{$class}{$key} = $v;
210 foreach my $class (@$all_settings) {
211 my $post_args = $posted{$class};
212 $post_args ||= {};
213 my $save_errors;
214 if ($post_args) {
215 my $sv = eval {
216 $class->save($u, $post_args);
218 if (my $err = $@) {
219 require Data::Dumper;
220 $save_errors = $err->field('map') if ref $err;
224 $returns{$class}{save_errors} = $save_errors;
225 $returns{$class}{post_args} = $post_args;
228 return \%returns;
231 sub save_had_errors {
232 my $class = shift;
233 my $save_rv = shift;
234 return 0 unless ref $save_rv;
236 my @settings = @_; # optional, for specific settings
237 @settings = keys %$save_rv unless @settings;
239 foreach my $setting (@settings) {
240 my $errors = $save_rv->{$setting}->{save_errors} || {};
241 return 1 if %$errors;
244 return 0;
247 sub errors_from_save {
248 my $class = shift;
249 my $save_rv = shift;
251 return $save_rv->{$class}->{save_errors};
254 sub args_from_save {
255 my $class = shift;
256 my $save_rv = shift;
258 return $save_rv->{$class}->{post_args};
261 sub ml {
262 my ($class, $code, $vars) = @_;
264 # can pass in a string and check 2 places in order:
265 # 1) setting.foo.text => general .setting.foo.text (overridden by current page)
266 # 2) setting.foo.text => general setting.foo.text (defined in en(_LJ).dat)
268 # whether passed with or without a ".", eat that immediately
269 $code =~ s/^\.//;
271 # 1) try with a ., for current page override in 'general' domain
272 # 2) try without a ., for global version in 'general' domain
273 foreach my $curr_code (".$code", $code) {
274 my $string = LJ::Lang::ml($curr_code, $vars);
275 return "" if $string eq "_none";
276 return $string unless LJ::Lang::is_missing_string($string);
279 # return the class name if we didn't find anything
280 $class =~ /.+::(\w+)$/;
281 return $1;
284 package LJ::Error::SettingSave;
285 use base 'LJ::Error';
287 sub user_caused { 1 }
288 sub fields { qw(map); } # key -> english (keys are LJ::Setting:: subclass-defined)
290 sub as_string {
291 my $self = shift;
292 my $map = $self->field('map');
293 return join(", ", map { $_ . '=' . $map->{$_} } sort keys %$map);