Bivio::Biz::Action::WikiValidator
# Copyright (c) 2009 bivio Software, Inc. All Rights Reserved.
# $Id$
package Bivio::Biz::Action::WikiValidator;
use strict;
use Bivio::Base 'Biz.Action';
b_use('IO.Trace');
our($_TRACE);
my($_D) = b_use('Bivio.Die');
my($_FP) = b_use('Type.FilePath');
my($_HS) = b_use('Type.HTTPStatus');
my($_RF) = b_use('Model.RealmFile');
my($_FCT) = b_use('FacadeComponent.Text');
my($_M) = b_use('Biz.Model');
my($_QUERY_KEY) = 'validate';
my($_T) = b_use('Agent.Task');
my($_V) = b_use('UI.View');
my($_WV) = b_use('Action.WikiView');
my($_A) = b_use('IO.Alert');
my($_TYPES) = [map(b_use("Type.$_"), qw(WikiName BlogFileName))];
my($_AA) = b_use('Action.Acknowledgement');
my($_R) = b_use('Agent.Request');
my($_PKG) = __PACKAGE__;
sub TYPE_LIST {
return @$_TYPES;
}
sub call_embedded_task {
my($proto, $uri, $wiki_state) = @_;
my($req) = $wiki_state->{req};
my($self) = ref($proto) ? $proto : $proto->unsafe_self_from_req($req)
|| $proto;
my($die);
_trace($wiki_state->{path}, ': ', $uri) if $_TRACE;
my($reply) = $_D->catch_quietly(
sub {return b_use('AgentEmbed.Dispatcher')->call_task($req, $uri)},
\$die,
);
return $self->validate_error($uri, $die, $wiki_state)
unless $reply;
$self->validate_error($uri, $_HS->new($reply->get('status')), $wiki_state)
unless $reply->is_status_ok;
return $reply;
}
sub error_txt {
my($self) = @_;
b_die('unable to load error list')
unless $self->unsafe_load_error_list;
return b_use('UI.View')->render('Wiki->validator_txt', $self->req);
}
sub get_current_or_new {
my($proto, $path, $realm_id, $req) = @_;
return $req->unsafe_get($proto->as_classloader_map_name)
|| $req->unsafe_from_query($_QUERY_KEY)
&& $req->can_user_execute_task('FORUM_WIKI_EDIT')
&& _new($proto, $path, $realm_id, $req)
|| $proto;
}
sub return_with_validate {
my($self, $task_return) = @_;
($task_return->{query} ||= {})->{$_QUERY_KEY} = 1;
return $task_return;
}
sub send_all_mail {
my($self, $email, $all_txt) = @_;
$self->put(
to_email => $email,
all_txt => $all_txt,
);
b_use('UI.View')->call_main('Wiki->validator_all_mail', $self->req);
return;
}
sub send_mail {
my($self, $email) = @_;
b_die('unable to load error list')
unless $self->unsafe_load_error_list;
$self->put(to_email => $email);
b_use('UI.View')->call_main('Wiki->validator_mail', $self->req);
return;
}
sub unsafe_load_error_list {
my($self) = @_;
return
unless ref($self) and my $e = $self->get('errors');
return
unless @$e;
return $_M->new($self->req, 'WikiErrorList')
->load_from_array($self->get('errors'));
}
sub validate_error {
my($self, $entity, $message, $wiki_state) = @_;
my($req) = $wiki_state && $wiki_state->{req} || $self->req;
if ($_D->is_blesser_of($message)
and my $prev_err = $message->get('attrs')->{$_PKG}
) {
$message = $prev_err->{message};
$entity = $prev_err->{entity};
}
$message = $_A->format_args(@$message)
if ref($message) eq 'ARRAY';
$message = $_FCT->facade_text_for_object(
$_D->is_blesser_of($message) ? $message->get('code') : $message, $req,
) if ref($message);
if ($message =~ s/(\w+(?:\:\:|\-\>)\w+.*)//) {
b_warn($message, ': removed Perl junk: ', $1, '; ', $req);
$_A->print_stack;
$message ||= 'internal server error';
}
my($err) = {
entity => $entity,
message => $message,
};
if ($wiki_state) {
my($cc) = $wiki_state->{calling_context};
$err->{path} = $cc->get('file'),
$err->{line_num} = $cc->get('line'),
}
$err->{path} ||= $self->unsafe_get('path');
_trace($err) if $_TRACE;
my($msg) = join(
'',
$err->{path} ? $err->{path} : (),
$err->{line_num} ? (', line ', $err->{line_num}) : (),
': ',
$entity ? ($entity, ': ') : (),
$message,
);
$_D->throw(DIE => {
msg => $msg,
$_PKG => $err,
}) if $wiki_state && $wiki_state->{die_on_validate_error};
return
unless ref($self);
my($re) = $self->get('ignore_regexp');
return
if $re && $msg =~ $re;
push(@{$self->get('errors')}, $err);
return;
}
sub validate_realm {
my($proto, $req) = @_;
$proto->delete_from_req($req);
my($die);
#TODO: Probably want call embedded_task
my($prev) = {map(($_ => $req->unsafe_get($_)), qw(task_id path_info query))};
my($self) = Bivio::Die->catch_quietly(
sub {_validate_realm($proto, $req)},
\$die,
);
$req->set_task_and_uri($prev);
($self = _new($proto, undef, undef, $req))->validate_error(undef, $die)
unless $self;
$self->put(errors => undef)
unless @{$self->get('errors')};
return $self
}
sub validate_uri {
my($self, $uri, $wiki_state) = @_;
return 1
unless ref($self);
return 1
if $self->get('uri_cache')->{$uri}++;
#TODO: check external links (could even check mailto: if local)
return 1
if $uri =~ /^\w+:/i;
$uri = _uri_root($self, $wiki_state->{req}) . $uri
unless $uri =~ m{^/};
#TODO: Need simpler check, because not always in correct context
return $self->call_embedded_task($uri, $wiki_state) ? 1 : 0;
}
sub _ignore_regexp {
my($realm_id, $req) = @_;
return !$realm_id || $realm_id eq $req->get('auth_id')
? $_M->new($req, 'WikiValidatorSettingList')
->regexp_for_auth_realm
: $req->with_realm($realm_id, sub {
return $_M->new($req, 'WikiValidatorSettingList')
->regexp_for_auth_realm;
});
}
sub _new {
my($proto, $path, $realm_id, $req) = @_;
return $proto->new({
path => $path,
uri_cache => {},
errors => [],
ignore_regexp => _ignore_regexp($realm_id, $req),
})->put_on_request($req);
}
sub _uri_root {
my($proto, $req) = @_;
my($uri) = ($req->unsafe_get('initial_uri') || '') =~ m{^.*?(/.*/)};
return $uri || '/';
}
sub _validate_path {
my($self, $type, $seen) = @_;
$_A->reset_warn_counter;
my($path) = $self->get('path');
my($p, $e) = $type =~ /Blog/ ? $type->from_literal($path)
: $_FP->get_tail($path);
unless ($type->is_valid($p)) {
$self->validate_error(undef, 'Not a valid Wiki or Blog name')
unless $type->is_ignored_value($path);
return;
}
$self->validate_error(
undef,
'Same name exists in Public and private areas',
) if $seen->{$p}++;
my($req) = $self->req;
my($die) = $_D->catch_quietly(sub {
$req->put(
query => undef,
path_info => undef,
)->delete('form_model');
if ($type =~ /Blog/) {
$req->set_task_and_uri({task_id => 'FORUM_BLOG_DETAIL', path_info => $p});
$_M->new($req, 'BlogList')->load_this({this => [$p]});
$_V->call_main('Blog->detail', $req);
}
else {
$req->set_task_and_uri({task_id => 'FORUM_WIKI_VIEW', path_info => $p});
$_WV->execute_prepare_html($req, undef, undef, $p);
$_V->call_main('Wiki->view', $req);
}
});
_trace($die) if $_TRACE;
$req->get('reply')->delete_output;
$self->validate_error(undef, $die)
if $die;
return;
}
sub _validate_realm {
my($proto, $req) = @_;
my($self) = _new($proto, undef, undef, $req);
$req->with_user(
$_M->new($req, 'RealmUser')->unsafe_get_any_online_admin,
sub {
$_M->new($req, 'BlogRecentList')->load_all;
foreach my $type (@$_TYPES) {
my($seen) = {};
my($paths) = $_M
->new($req, $type =~ /Blog/ ? 'BlogList' : 'WikiList')
->map_iterate(sub {shift->get('RealmFile.path')});
foreach my $path (@$paths) {
$self->put(path => $path);
_validate_path($self, $type, $seen);
}
}
return;
},
);
return $self;
}
1;