package LJ; use strict; use Class::Autouse qw( LJ::ModuleLoader ); my $hooks_dir_scanned = 0; # bool: if we've loaded everything from cgi-bin/LJ/Hooks/ # # name: LJ::are_hooks # des: Returns true if the site has one or more hooks installed for # the given hookname. # args: hookname # sub are_hooks { my $hookname = shift; load_hooks_dir() unless $hooks_dir_scanned; return defined $LJ::HOOKS{$hookname}; } # # name: LJ::clear_hooks # des: Removes all hooks. # sub clear_hooks { %LJ::HOOKS = (); $hooks_dir_scanned = 0; } # # name: LJ::run_hooks # des: Runs all the site-specific hooks of the given name. # returns: list of arrayrefs, one for each hook ran, their # contents being their own return values. # args: hookname, args* # des-args: Arguments to be passed to hook. # sub run_hooks { my ($hookname, @args) = @_; load_hooks_dir() unless $hooks_dir_scanned; my @ret; foreach my $hook (@{$LJ::HOOKS{$hookname} || []}) { push @ret, [ $hook->(@args) ]; } return @ret; } # # name: LJ::run_hook # des: Runs single site-specific hook of the given name. # returns: return value from hook # args: hookname, args* # des-args: Arguments to be passed to hook. # sub run_hook { my ($hookname, @args) = @_; load_hooks_dir() unless $hooks_dir_scanned; return undef unless @{$LJ::HOOKS{$hookname} || []}; return $LJ::HOOKS{$hookname}->[0]->(@args); } # # name: LJ::register_hook # des: Installs a site-specific hook. # info: Installing multiple hooks per hookname is valid. # They're run later in the order they're registered. # args: hookname, subref # des-subref: Subroutine reference to run later. # sub register_hook { my $hookname = shift; my $subref = shift; push @{$LJ::HOOKS{$hookname}}, $subref; } sub load_hooks_dir { return if $hooks_dir_scanned++; # eh, not actually subclasses... just files: foreach my $class (LJ::ModuleLoader->module_subclasses("LJ::Hooks")) { eval "use $class;"; die "Error loading $class: $@" if $@; } } # # name: LJ::register_setter # des: Installs code to run for the "set" command in the console. # info: Setters can be general or site-specific. # args: key, subref # des-key: Key to set. # des-subref: Subroutine reference to run later. # sub register_setter { my $key = shift; my $subref = shift; $LJ::SETTER{$key} = $subref; } register_setter('synlevel', sub { my ($u, $key, $value, $err) = @_; unless ($value =~ /^(title|summary|full)$/) { $$err = "Illegal value. Must be 'title', 'summary', or 'full'"; return 0; } $u->set_prop("opt_synlevel", $value); return 1; }); register_setter("newpost_minsecurity", sub { my ($u, $key, $value, $err) = @_; unless ($value =~ /^(public|friends|private)$/) { $$err = "Illegal value. Must be 'public', 'friends', or 'private'"; return 0; } # Don't let commmunities be private if ($u->{'journaltype'} eq "C" && $value eq "private") { $$err = "newpost_minsecurity cannot be private for communities"; return 0; } $value = "" if $value eq "public"; $u->set_prop("newpost_minsecurity", $value); return 1; }); register_setter("stylesys", sub { my ($u, $key, $value, $err) = @_; unless ($value =~ /^[sS]?(1|2)$/) { $$err = "Illegal value. Must be S1 or S2."; return 0; } $value = $1 + 0; $u->set_prop("stylesys", $value); return 1; }); register_setter("maximagesize", sub { my ($u, $key, $value, $err) = @_; unless ($value =~ m/^(\d+)[x,|](\d+)$/) { $$err = "Illegal value. Must be width,height."; return 0; } $value = "$1|$2"; $u->set_prop("opt_imagelinks", $value); return 1; }); register_setter("opt_ljcut_disable_lastn", sub { my ($u, $key, $value, $err) = @_; unless ($value =~ /^(0|1)$/) { $$err = "Illegal value. Must be '0' or '1'"; return 0; } $u->set_prop("opt_ljcut_disable_lastn", $value); return 1; }); register_setter("opt_ljcut_disable_friends", sub { my ($u, $key, $value, $err) = @_; unless ($value =~ /^(0|1)$/) { $$err = "Illegal value. Must be '0' or '1'"; return 0; } $u->set_prop("opt_ljcut_disable_friends", $value); return 1; }); register_setter("disable_quickreply", sub { my ($u, $key, $value, $err) = @_; unless ($value =~ /^(0|1)$/) { $$err = "Illegal value. Must be '0' or '1'"; return 0; } $u->set_prop("opt_no_quickreply", $value); return 1; }); register_setter("disable_nudge", sub { my ($u, $key, $value, $err) = @_; unless ($value =~ /^(0|1)$/) { $$err = "Illegal value. Must be '0' or '1'"; return 0; } $u->set_prop("opt_no_nudge", $value); return 1; }); register_setter("trusted_s1", sub { my ($u, $key, $value, $err) = @_; unless ($value =~ /^(\d+,?)+$/) { $$err = "Illegal value. Must be a comma separated list of style ids"; return 0; } # guard against accidentally nuking an existing value. my $propval = $u->prop("trusted_s1"); if ($value && $propval) { $$err = "You already have this property set to '$propval'. To overwrite this value,\n" . "first clear the property ('set trusted_s1 0'). Then, set the new value or store\n". "multiple values (with 'set trusted_s1 $propval,$value')."; return 0; } $u->set_prop("trusted_s1", $value); return 1; }); register_setter("icbm", sub { my ($u, $key, $value, $err) = @_; my $loc = eval { LJ::Location->new(coords => $value); }; unless ($loc) { $u->set_prop("icbm", ""); # unset $$err = "Illegal value. Not a recognized format." if $value; return 0; } $u->set_prop("icbm", $loc->as_posneg_comma); return 1; }); register_setter("no_mail_alias", sub { my ($u, $key, $value, $err) = @_; unless ($value =~ /^[01]$/) { $$err = "Illegal value. Must be '0' or '1'."; return 0; } my $dbh = LJ::get_db_writer(); if ($value) { $dbh->do("DELETE FROM email_aliases WHERE alias=?", undef, "$u->{'user'}\@$LJ::USER_DOMAIN"); } elsif ($u->{'status'} eq "A" && LJ::get_cap($u, "useremail")) { $dbh->do("REPLACE INTO email_aliases (alias, rcpt) VALUES (?,?)", undef, "$u->{'user'}\@$LJ::USER_DOMAIN", $u->email_raw); } $u->set_prop("no_mail_alias", $value); return 1; }); register_setter("latest_optout", sub { my ($u, $key, $value, $err) = @_; unless ($value =~ /^(?:yes|no)$/i) { $$err = "Illegal value. Must be 'yes' or 'no'."; return 0; } $value = lc $value eq 'yes' ? 1 : 0; $u->set_prop("latest_optout", $value); return 1; }); 1;