package BSE::Edit::Article;
use strict;
use base qw(BSE::Edit::Base);
-use BSE::Util::Tags qw(tag_error_img);
+use BSE::Util::Tags qw(tag_error_img tag_article tag_object);
use BSE::Util::SQL qw(now_sqldate now_sqldatetime);
use BSE::Permissions;
-use DevHelp::HTML qw(:default popup_menu);
+use BSE::Util::HTML qw(:default popup_menu);
use BSE::Arrows;
-use BSE::CfgInfo qw(custom_class admin_base_url cfg_image_dir);
+use BSE::CfgInfo qw(custom_class admin_base_url cfg_image_dir cfg_dist_image_uri cfg_image_uri);
use BSE::Util::Iterate;
use BSE::Template;
use BSE::Util::ContentType qw(content_type);
+use BSE::Regen 'generate_article';
use DevHelp::Date qw(dh_parse_date dh_parse_sql_date);
+use List::Util qw(first);
use constant MAX_FILE_DISPLAYNAME_LENGTH => 255;
+use constant ARTICLE_CUSTOM_FIELDS_CFG => "article custom fields";
+
+our $VERSION = "1.055";
=head1 NAME
addimg => 'add_image',
a_edit_image => 'req_edit_image',
a_save_image => 'req_save_image',
+ a_order_images => 'req_order_images',
remove => 'remove',
showimages => 'show_images',
process => 'save_image_changes',
a_tree => 'req_tree',
a_article => 'req_article',
a_config => 'req_config',
+ a_restepkid => 'req_restepkid',
);
}
return $article->{parentid} && $parent &&
($article->{parentid} == $shopid ||
- $parent->{generator} eq 'Generate::Catalog');
+ $parent->{generator} eq 'BSE::Generate::Catalog');
}
sub possible_parents {
my $shopid = $self->cfg->entryErr('articles', 'shop');
my @parents = $articles->getBy('level', $article->{level}-1);
- @parents = grep { $_->{generator} eq 'Generate::Article'
+ @parents = grep { $_->{generator} eq 'BSE::Generate::Article'
&& $_->{id} != $shopid } @parents;
# user can only select parent they can add to
else {
if ($req->user_can('edit_add_child')) {
push @values, -1;
- $labels{-1} = "-- move up a level -- become a section";
+ $labels{-1} = $req->catmsg("msg:bse/admin/edit/uplabelsect");
}
}
}
my @children;
$article->{id} or return;
- if (UNIVERSAL::isa($article, 'Article')) {
+ if (UNIVERSAL::isa($article, 'BSE::TB::Article')) {
@children = $article->children;
}
elsif ($article->{id}) {
$default = $article->{template};
}
else {
- my @options;
- $default = $self->default_template($article, $cfg, \@templates);
+ my @template_names = map $_->{name}, @templates;
+ $default = $self->default_template($article, $cfg, \@template_names);
}
my %labels =
(
@templates;
}
+sub categories {
+ my ($self, $articles) = @_;
+
+ return $articles->categories;
+}
+
sub edit_parent {
my ($article) = @_;
sub _load_step_kids {
my ($article, $step_kids) = @_;
- my @stepkids = OtherParents->getBy(parentId=>$article->{id}) if $article->{id};
+ require BSE::TB::OtherParents;
+ my @stepkids = BSE::TB::OtherParents->getBy(parentId=>$article->{id}) if $article->{id};
%$step_kids = map { $_->{childId} => $_ } @stepkids;
$step_kids->{loaded} = 1;
}
return unless $article->{id} && $article->{id} > 0;
- OtherParents->getBy(childId=>$article->{id});
+ require BSE::TB::OtherParents;
+ BSE::TB::OtherParents->getBy(childId=>$article->{id});
}
sub tag_ifStepParents {
$urladd = '' unless defined $urladd;
my $cgi_uri = $self->{cfg}->entry('uri', 'cgi', '/cgi-bin');
- my $images_uri = $self->{cfg}->entry('uri', 'images', '/images');
+ my $images_uri = cfg_dist_image_uri();
my $html = '';
my $url = $ENV{SCRIPT_NAME} . "?id=$article->{id}";
if ($cgi->param('_t')) {
$urladd = '' unless defined $urladd;
my $cgi_uri = $self->{cfg}->entry('uri', 'cgi', '/cgi-bin');
- my $images_uri = $self->{cfg}->entry('uri', 'images', '/images');
+ my $images_uri = cfg_dist_image_uri();
my $urlbase = admin_base_url($req->cfg);
my $refresh_url = "$urlbase$ENV{SCRIPT_NAME}?id=$article->{id}";
my $t = $req->cgi->param('_t');
return make_arrows($req->cfg, $down_url, $up_url, $refresh_url, $img_prefix);
}
+sub tag_category {
+ my ($self, $articles, $article) = @_;
+
+ my @cats = $self->categories($articles);
+
+ my %labels = map { $_->{id}, $_->{name} } @cats;
+
+ return popup_menu(-name => 'category',
+ -values => [ map $_->{id}, @cats ],
+ -labels => \%labels,
+ -default => $article->{category});
+}
+
sub tag_edit_link {
my ($cfg, $article, $args, $acts, $funcname, $templater) = @_;
my ($which, $name) = split / /, $args, 2;
(
geo => $args,
cfg => $cfg,
+ nolink => 1,
);
}
}
}
+sub iter_tags {
+ my ($self, $article) = @_;
+
+ $article->{id}
+ or return;
+
+ return $article->tag_objects;
+}
+
+my %base_custom_validation =
+ (
+ customDate1 =>
+ {
+ rules => "date",
+ htmltype => "text",
+ width => 10,
+ default => "",
+ type => "date",
+ },
+ customDate2 =>
+ {
+ rules => "date",
+ htmltype => "text",
+ width => 10,
+ default => "",
+ type => "date",
+ },
+ customStr1 =>
+ {
+ htmltype => "text",
+ default => "",
+ },
+ customStr2 =>
+ {
+ htmltype => "text",
+ default => "",
+ },
+ customInt1 =>
+ {
+ rules => "integer",
+ htmltype => "text",
+ width => 10,
+ default => "",
+ },
+ customInt2 =>
+ {
+ rules => "integer",
+ htmltype => "text",
+ width => 10,
+ default => "",
+ },
+ customInt3 =>
+ {
+ rules => "integer",
+ htmltype => "text",
+ width => 10,
+ default => "",
+ },
+ customInt4 =>
+ {
+ rules => "integer",
+ htmltype => "text",
+ width => 10,
+ default => "",
+ },
+ );
+
+sub custom_fields {
+ my $self = shift;
+
+ require DevHelp::Validate;
+ DevHelp::Validate->import;
+ return DevHelp::Validate::dh_configure_fields
+ (
+ \%base_custom_validation,
+ $self->cfg,
+ ARTICLE_CUSTOM_FIELDS_CFG,
+ BSE::DB->single->dbh,
+ );
+}
+
+sub _custom_fields {
+ my $self = shift;
+
+ my $fields = $self->custom_fields;
+ my %active;
+ for my $key (keys %$fields) {
+ $fields->{$key}{description}
+ and $active{$key} = $fields->{$key};
+ }
+
+ return \%active;
+}
+
sub low_edit_tags {
my ($self, $acts, $request, $article, $articles, $msg, $errors) = @_;
my $cgi = $request->cgi;
my $show_full = $cgi->param('f_showfull');
my $if_error = $msg || ($errors && keys %$errors) || $request->cgi->param("_e");
- $msg ||= join "\n", map escape_html($_), $cgi->param('message'), $cgi->param('m');
- $msg ||= $request->message($errors);
+ #$msg ||= join "\n", map escape_html($_), $cgi->param('message'), $cgi->param('m');
+ $msg .= $request->message($errors);
my $parent;
if ($article->{id}) {
if ($article->{parentid} > 0) {
else {
$parent = { title=>"How did we get here?", id=>0 };
}
+ $request->set_article(article => $article);
+ $request->set_variable(ifnew => !$article->{id});
my $cfg = $self->{cfg};
my $mbcs = $cfg->entry('html', 'mbcs', 0);
my $tag_hash = $mbcs ? \&tag_hash_mbcs : \&tag_hash;
my @groups;
my $current_group;
my $it = BSE::Util::Iterate->new;
+ my $ito = BSE::Util::Iterate::Objects->new;
+ my $ita = BSE::Util::Iterate::Article->new(req => $request);
+
+ my $custom = $self->_custom_fields;
+ # only return the fields that are defined
+ $request->set_variable(custom => $custom);
+ $request->set_variable(errors => $errors || {});
+ my $article_type = $cfg->entry('level names', $article->{level}, 'Article');
+ $request->set_variable(article_type => $article_type);
+
return
(
$request->admin_tags,
- article => [ $tag_hash, $article ],
+ article => sub { tag_article($article, $cfg, $_[0]) },
old => [ \&tag_old, $article, $cgi ],
default => [ \&tag_default, $self, $request, $article ],
- articleType => [ \&tag_art_type, $article->{level}, $cfg ],
+ articleType => escape_html($article_type),
parentType => [ \&tag_art_type, $article->{level}-1, $cfg ],
ifNew => [ \&tag_if_new, $article ],
list => [ \&tag_list, $self, $article, $articles, $cgi, $request ],
imgmove => [ \&tag_imgmove, $request, $article, \$image_index, \@images ],
message => $msg,
ifError => $if_error,
- DevHelp::Tags->make_iterator2
- ([ \&iter_get_kids, $article, $articles ],
- 'child', 'children', \@children, \$child_index),
+ $ita->make
+ (
+ code => [ \&iter_get_kids, $article, $articles ],
+ single => 'child',
+ plural => 'children',
+ data => \@children,
+ index => \$child_index,
+ ),
ifchildren => \&tag_if_children,
childtype => [ \&tag_art_type, $article->{level}+1, $cfg ],
ifHaveChildType => [ \&tag_if_have_child_type, $article->{level}, $cfg ],
templates => [ \&tag_templates, $self, $article, $cfg, $cgi ],
titleImages => [ \&tag_title_images, $self, $article, $cfg, $cgi ],
editParent => [ \&tag_edit_parent, $article ],
- DevHelp::Tags->make_iterator2
- ([ \&iter_allkids, $article ], 'kid', 'kids', \@allkids, \$allkid_index),
+ $ita->make
+ (
+ code => [ \&iter_allkids, $article ],
+ single => 'kid',
+ plural => 'kids',
+ data => \@allkids,
+ index => \$allkid_index,
+ ),
ifStepKid =>
[ \&tag_if_step_kid, $article, \@allkids, \$allkid_index, \%stepkids ],
stepkid => [ \&tag_step_kid, $article, \@allkids, \$allkid_index,
ifPossibles =>
[ \&tag_if_possible_stepkids, \%stepkids, $request, $article,
\@possstepkids, $articles, $cgi ],
- DevHelp::Tags->make_iterator2
- ( [ \&iter_get_stepparents, $article ], 'stepparent', 'stepparents',
- \@stepparents, \$stepparent_index),
+ $ita->make
+ (
+ code => [ \&iter_get_stepparents, $article ],
+ single => 'stepparent',
+ plural => 'stepparents',
+ data => \@stepparents,
+ index => \$stepparent_index,
+ ),
ifStepParents => \&tag_ifStepParents,
stepparent_targ =>
[ \&tag_stepparent_targ, $article, \@stepparent_targs,
stepparent_possibles =>
[ \&tag_stepparent_possibles, $cgi, $request, $article, $articles,
\@stepparent_targs, \@stepparentpossibles, ],
- DevHelp::Tags->make_iterator2
- ([ iter_files => $self, $article ], 'file', 'files', \@files, \$file_index ),
+ $ito->make
+ (
+ code => [ iter_files => $self, $article ],
+ single => 'file',
+ plural => 'files',
+ data => \@files,
+ index => \$file_index,
+ ),
movefiles =>
[ \&tag_movefiles, $self, $request, $article, \@files, \$file_index ],
$it->make
single => "file_meta",
nocache => 1,
),
+ ifFileExists => sub {
+ @files && $file_index >= 0 && $file_index < @files
+ or return 0;
+
+ return -f ($files[$file_index]->full_filename($cfg));
+ },
file_display => [ tag_file_display => $self, \@files, \$file_index ],
DevHelp::Tags->make_iterator2
(\&iter_admin_users, 'iadminuser', 'adminusers'),
error => [ $tag_hash, $errors ],
error_img => [ \&tag_error_img, $cfg, $errors ],
ifFieldPerm => [ \&tag_if_field_perm, $request, $article ],
- parent => [ $tag_hash, $parent ],
+ parent => [ \&tag_article, $parent, $cfg ],
DevHelp::Tags->make_iterator2
([ \&iter_flags, $self ], 'flag', 'flags' ),
ifFlagSet => [ \&tag_if_flag_set, $article ],
$it->make_iterator([ iter_file_stores => $self],
'file_store', 'file_stores'),
ifGroupRequired => [ \&tag_ifGroupRequired, $article, \$current_group ],
+ category => [ tag_category => $self, $articles, $article ],
+ $ito->make
+ (
+ single => "tag",
+ plural => "tags",
+ code => [ iter_tags => $self, $article ],
+ ),
);
}
sub tag_ifGroupRequired {
my ($article, $rgroup) = @_;
+ $article->{id}
+ or return 0;
+
$$rgroup or return 0;
$article->is_accessible_to($$rgroup);
}
my %article;
- my @cols = Article->columns;
+ my @cols = BSE::TB::Article->columns;
@article{@cols} = ('') x @cols;
$article{id} = '';
$article{parentid} = $parentid;
return;
}
- return \%article;
+ return $self->_make_dummy_article(\%article);
+}
+
+sub _make_dummy_article {
+ my ($self, $article) = @_;
+
+ require BSE::DummyArticle;
+ return bless $article, "BSE::DummyArticle";
}
sub add_form {
return $self->low_edit_form($req, $article, $articles, $msg, $errors);
}
-sub generator { 'Generate::Article' }
+sub generator { 'BSE::Generate::Article' }
sub typename {
my ($self) = @_;
$errors->{linkAlias} = "Link alias must contain only alphanumerics and contain at least one letter";
}
}
+
+ if (defined $data->{category}) {
+ unless (first { $_->{id} eq $data->{category} } $self->categories($articles)) {
+ $errors->{category} = "msg:bse/admin/edit/category/unknown";
+ }
+ }
+
+ require DevHelp::Validate;
+ DevHelp::Validate->import('dh_validate_hash');
+ dh_validate_hash($data, $errors,
+ {
+ fields => $self->_custom_fields,
+ optional => 1,
+ dbh => BSE::DB->single->dbh,
+ },
+ $self->cfg, ARTICLE_CUSTOM_FIELDS_CFG);
}
sub validate {
sub fill_new_data {
my ($self, $req, $data, $articles) = @_;
+ my $custom = $self->_custom_fields;
+ for my $key (keys %$custom) {
+ my ($value) = $req->cgi->param($key);
+ if (defined $value) {
+ if ($key =~ /^customDate/) {
+ require DevHelp::Date;
+ my $msg;
+ if (my ($year, $month, $day) =
+ DevHelp::Date::dh_parse_date($value, \$msg)) {
+ $data->{$key} = sprintf("%04d-%02d-%02d", $year, $month, $day);
+ }
+ else {
+ $data->{$key} = undef;
+ }
+ }
+ elsif ($key =~ /^customInt/) {
+ if ($value =~ /\S/) {
+ $data->{$key} = $value;
+ }
+ else {
+ $data->{$key} = undef;
+ }
+ }
+ else {
+ $data->{$key} = $value;
+ }
+ }
+ }
+
custom_class($self->{cfg})
->article_fill_new($data, $self->typename);
sub make_link {
my ($self, $article) = @_;
+ $article->is_linked
+ or return "";
+
+ my $title = $article->title;
if ($article->is_dynamic) {
- return "/cgi-bin/page.pl?page=$article->{id}&title=".escape_uri($article->{title});
+ (my $extra = $title) =~ tr/A-Za-z0-9/-/sc;
+ return "/cgi-bin/page.pl?page=$article->{id}&title=".escape_uri($extra);
}
my $article_uri = $self->link_path($article);
my $link = "$article_uri/$article->{id}.html";
my $link_titles = $self->{cfg}->entryBool('basic', 'link_titles', 0);
if ($link_titles) {
- (my $extra = lc $article->{title}) =~ tr/a-z0-9/_/sc;
+ (my $extra = $title) =~ tr/A-Za-z0-9/-/sc;
$link .= "/" . $extra . "_html";
}
$link;
}
+sub save_columns {
+ my ($self, $table_object) = @_;
+
+ my @columns = $table_object->rowClass->columns;
+ shift @columns;
+
+ return @columns;
+}
+
+sub _validate_tags {
+ my ($self, $tags, $errors) = @_;
+
+ my $fail = 0;
+ my @errors;
+ for my $tag (@$tags) {
+ my $error;
+ if ($tag =~ /\S/
+ && !BSE::TB::Tags->valid_name($tag, \$error)) {
+ push @errors, "msg:bse/admin/edit/tags/invalid/$error";
+ $errors->{tags} = \@errors;
+ ++$fail;
+ }
+ else {
+ push @errors, undef;
+ }
+ }
+
+ return $fail;
+}
+
sub save_new {
my ($self, $req, $article, $articles) = @_;
my $cgi = $req->cgi;
my %data;
my $table_object = $self->table_object($articles);
- my @columns = $table_object->rowClass->columns;
+ my @columns = $self->save_columns($table_object);
$self->save_thumbnail($cgi, undef, \%data);
for my $name (@columns) {
$data{$name} = $cgi->param($name)
$errors{parentid} = "Invalid parent selection (template bug)";
}
$self->validate(\%data, $articles, \%errors);
+
+ my $save_tags = $cgi->param("_save_tags");
+ my @tags;
+ if ($save_tags) {
+ @tags = $cgi->param("tags");
+ $self->_validate_tags(\@tags, \%errors);
+ }
+
+ my $meta;
+ if ($cgi->param("_save_meta")) {
+ require BSE::ArticleMetaMeta;
+ $meta = BSE::ArticleMetaMeta->retrieve($req, $article, \%errors);
+ }
+
if (keys %errors) {
if ($req->is_ajax) {
return $req->json_content
# end adrian
$self->fill_new_data($req, \%data, $articles);
- for my $col (qw(titleImage imagePos template keyword menu titleAlias linkAlias body author summary)) {
+ for my $col (qw(titleImage imagePos template keyword menu titleAlias linkAlias body author summary category)) {
defined $data{$col}
or $data{$col} = $self->default_value($req, \%data, $col);
}
or $data{$col} = $self->default_value($req, \%data, $col);
}
- shift @columns;
- $article = $table_object->add(@data{@columns});
+ my @cols = $table_object->rowClass->columns;
+ shift @cols;
+
+ # fill out anything else from defaults
+ for my $col (@columns) {
+ exists $data{$col}
+ or $data{$col} = $self->default_value($req, \%data, $col);
+ }
+
+ $article = $table_object->add(@data{@cols});
+
+ $self->save_new_more($req, $article, \%data);
# we now have an id - generate the links
my ($after_id) = $cgi->param("_after");
if (defined $after_id) {
- Articles->reorder_child($article->{parentid}, $article->{id}, $after_id);
+ BSE::TB::Articles->reorder_child($article->{parentid}, $article->{id}, $after_id);
# reload, the displayOrder probably changed
$article = $articles->getByPkey($article->{id});
}
- use Util 'generate_article';
+ if ($save_tags) {
+ my $error;
+ $article->set_tags([ grep /\S/, @tags ], \$error);
+ }
+
+ if ($meta) {
+ BSE::ArticleMetaMeta->save($article, $meta);
+ }
+
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
if ($req->is_ajax) {
$data->{body} =~ s/\x0D\x0A/\n/g;
$data->{body} =~ tr/\r/\n/;
}
- for my $col (Article->columns) {
+ for my $col (BSE::TB::Article->columns) {
next if $col =~ /^custom/;
$article->{$col} = $data->{$col}
if exists $data->{$col} && $col ne 'id' && $col ne 'parentid';
}
+ my $custom = $self->_custom_fields;
+ for my $key (keys %$custom) {
+ if (exists $data->{$key}) {
+ if ($key =~ /^customDate/) {
+ require DevHelp::Date;
+ my $msg;
+ if (my ($year, $month, $day) =
+ DevHelp::Date::dh_parse_date($data->{$key}, \$msg)) {
+ $article->set($key, sprintf("%04d-%02d-%02d", $year, $month, $day));
+ }
+ else {
+ $article->set($key => undef);
+ }
+ }
+ elsif ($key =~ /^customInt/) {
+ if ($data->{$key} =~ /\S/) {
+ $article->set($key => $data->{$key});
+ }
+ else {
+ $article->set($key => undef);
+ }
+ }
+ else {
+ $article->set($key => $data->{$key});
+ }
+ }
+ }
custom_class($self->{cfg})
->article_fill_old($article, $data, $self->typename);
[
map $_->data_only, $article->files,
];
+ $article_data->{tags} =
+ [
+ $article->tags, # just the names
+ ];
return $article_data;
}
+sub save_more {
+ my ($self, $req, $article, $data) = @_;
+ # nothing to do here
+}
+
+sub save_new_more {
+ my ($self, $req, $article, $data) = @_;
+ # nothing to do here
+}
+
=item save
Error codes:
my $old_dynamic = $article->is_dynamic;
my $cgi = $req->cgi;
my %data;
- for my $name ($article->columns) {
+ my $table_object = $self->table_object($articles);
+ my @save_cols = $self->save_columns($table_object);
+ for my $name (@save_cols) {
$data{$name} = $cgi->param($name)
if defined($cgi->param($name)) and $name ne 'id' && $name ne 'parentid'
&& $req->user_can("edit_field_edit_$name", $article);
$article->{template} =~ m|\.\.|) {
$errors{template} = "Please only select templates from the list provided";
}
+
+ my $meta;
+ if ($cgi->param("_save_meta")) {
+ require BSE::ArticleMetaMeta;
+ $meta = BSE::ArticleMetaMeta->retrieve($req, $article, \%errors);
+ }
+
+ my $save_tags = $cgi->param("_save_tags");
+ my @tags;
+ if ($save_tags) {
+ @tags = $cgi->param("tags");
+ $self->_validate_tags(\@tags, \%errors);
+ }
$self->validate_old($article, \%data, $articles, \%errors, scalar $req->is_ajax)
or return $self->_service_error($req, $article, $articles, undef, \%errors, "FIELD");
$self->save_thumbnail($cgi, $article, \%data)
if $req->user_can('edit_field_edit_thumbImage', $article);
+ if (exists $data{flags} && $data{flags} =~ /D/) {
+ $article->remove_html;
+ }
$self->fill_old_data($req, $article, \%data);
# reparenting
$article->{expire} = sql_date($cgi->param('expire')) || $Constants::D_99
if defined $cgi->param('expire') &&
$req->user_can('edit_field_edit_expire', $article);
- $article->{lastModified} = now_sqldatetime();
for my $col (qw/force_dynamic inherit_siteuser_rights/) {
if ($req->user_can("edit_field_edit_$col", $article)
&& $cgi->param("save_$col")) {
}
}
-# Added by adrian
- my $user = $req->getuser;
- $article->{lastModifiedBy} = $user ? $user->{logon} : '';
-# end adrian
+ $article->mark_modified(actor => $req->getuser || "U");
my @save_group_ids = $cgi->param('save_group_id');
if ($req->user_can('edit_field_edit_group_id')
my $old_link = $article->{link};
# this need to go last
$article->update_dynamic($self->{cfg});
- if ($article->{link} &&
- !$self->{cfg}->entry('protect link', $article->{id})) {
+ if (!$self->{cfg}->entry('protect link', $article->{id})) {
my $article_uri = $self->make_link($article);
$article->setLink($article_uri);
}
$article->save();
+ if ($save_tags) {
+ my $error;
+ $article->set_tags([ grep /\S/, @tags ], \$error);
+ }
+
+use Data::Dumper;
+print STDERR Dumper($meta);
+ if ($meta) {
+ BSE::ArticleMetaMeta->save($article, $meta);
+ }
+
# fix the kids too
my @extra_regen;
@extra_regen = $self->update_child_dynamic($article, $articles, $req);
my ($after_id) = $cgi->param("_after");
if (defined $after_id) {
- Articles->reorder_child($article->{parentid}, $article->{id}, $after_id);
+ BSE::TB::Articles->reorder_child($article->{parentid}, $article->{id}, $after_id);
# reload, the displayOrder probably changed
$article = $articles->getByPkey($article->{id});
}
- use Util 'generate_article';
if ($Constants::AUTO_GENERATE) {
generate_article($articles, $article);
for my $regen_id (@extra_regen) {
my $regen = $articles->getByPkey($regen_id);
- Util::generate_low($articles, $regen, $self->{cfg});
+ BSE::Regen::generate_low($articles, $regen, $self->{cfg});
}
}
+ $self->save_more($req, $article, \%data);
+
if ($req->is_ajax) {
return $req->json_content
(
sub can_reparent_to {
my ($self, $article, $newparent, $parent_editor, $articles, $rmsg) = @_;
- $DB::single = 1;
-
my @child_types = $parent_editor->child_types;
if (!grep $_ eq ref $self, @child_types) {
my ($child_type) = (ref $self) =~ /(\w+)$/;
unlink("$imagedir/$original->{thumbImage}");
@$newdata{qw/thumbImage thumbWidth thumbHeight/} = ('', 0, 0);
}
- my $image = $cgi->param('thumbnail');
- if ($image && -s $image) {
+ my $image_name = $cgi->param('thumbnail');
+ my $image = $cgi->upload('thumbnail');
+ if ($image_name && -s $image) {
# where to put it...
my $name = '';
- $image =~ /([\w.-]+)$/ and $name = $1;
+ $image_name =~ /([\w.-]+)$/ and $name = $1;
my $filename = time . "_" . $name;
use Fcntl;
close OUTPUT
or die "Could not close image output file: $!";
- use Image::Size;
+ require BSE::ImageSize;
if ($original && $original->{thumbImage}) {
#unlink("$imagedir/$original->{thumbImage}");
}
- @$newdata{qw/thumbWidth thumbHeight/} = imgsize("$imagedir/$filename");
+ @$newdata{qw/thumbWidth thumbHeight/} =
+ BSE::ImageSize::imgsize("$imagedir/$filename");
$newdata->{thumbImage} = $filename;
}
}
return ( 'BSE::Edit::Article' );
}
+=item add_stepkid
+
+Add a step child to an article.
+
+Parameters:
+
+=over
+
+=item *
+
+id - parent article id (required)
+
+=item *
+
+stepkid - child article id (required)
+
+=item *
+
+_after - id of the allkid of id to position the stepkid after
+(optional)
+
+=back
+
+Returns a FIELD error for an invalid stepkid.
+
+Returns an ACCESS error for insufficient access.
+
+Return an ADD error for a general add failure.
+
+On success returns:
+
+ {
+ success: 1,
+ relationship: { childId: I<childid>, parentId: I<parentid> }
+ }
+
+=back
+
+=cut
+
sub add_stepkid {
my ($self, $req, $article, $articles) = @_;
or return $self->csrf_error($req, $article, "admin_add_stepkid", "Add Stepkid");
$req->user_can(edit_stepkid_add => $article)
- or return $self->edit_form($req, $article, $articles,
- "You don't have access to add step children to this article");
+ or return $self->_service_error($req, $article, $articles,
+ "You don't have access to add step children to this article", {}, "ACCESS");
my $cgi = $req->cgi;
- require 'BSE/Admin/StepParents.pm';
- eval {
- my $childId = $cgi->param('stepkid');
- defined $childId
- or die "No stepkid supplied to add_stepkid";
+ require BSE::Admin::StepParents;
+
+ my %errors;
+ my $childId = $cgi->param('stepkid');
+ defined $childId
+ or $errors{stepkid} = "No stepkid supplied to add_stepkid";
+ unless ($errors{stepkid}) {
$childId =~ /^\d+$/
- or die "Invalid stepkid supplied to add_stepkid";
- my $child = $articles->getByPkey($childId)
- or die "Article $childId not found";
+ or $errors{stepkid} = "Invalid stepkid supplied to add_stepkid";
+ }
+ my $child;
+ unless ($errors{stepkid}) {
+ $child = $articles->getByPkey($childId)
+ or $errors{stepkid} = "Article $childId not found";
+ }
+ keys %errors
+ and return $self->_service_error
+ ($req, $article, $articles, $errors{stepkid}, \%errors, "FIELD");
- $req->user_can(edit_stepparent_add => $child)
- or die "You don't have access to add a stepparent to that article\n";
+ $req->user_can(edit_stepparent_add => $child)
+ or return $self->_service_error($req, $article, $articles, "You don't have access to add a stepparent to that article", {}, "ACCESS");
+
+ my $new_entry;
+ eval {
my $release = $cgi->param('release');
dh_parse_date($release) or $release = undef;
my $expire = $cgi->param('expire');
dh_parse_date($expire) or $expire = undef;
- my $newentry =
+ $new_entry =
BSE::Admin::StepParents->add($article, $child, $release, $expire);
};
if ($@) {
- return $self->edit_form($req, $article, $articles, $@);
+ return $self->_service_error($req, $article, $articles, $@, {}, "ADD");
+ }
+
+ my $after_id = $cgi->param("_after");
+ if (defined $after_id) {
+ BSE::TB::Articles->reorder_child($article->id, $child->id, $after_id);
}
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
- return $self->refresh($article, $cgi, 'step', 'Stepchild added');
+ if ($req->is_ajax) {
+ return $req->json_content
+ (
+ success => 1,
+ relationship => $new_entry->data_only,
+ );
+ }
+ else {
+ $self->refresh($article, $cgi, 'step', 'Stepchild added');
+ }
}
+=item del_stepkid
+
+Remove a stepkid relationship.
+
+Parameters:
+
+=over
+
+=item *
+
+id - parent article id (required)
+
+=item *
+
+stepkid - child article id (required)
+
+=back
+
+Returns a FIELD error for an invalid stepkid.
+
+Returns an ACCESS error for insufficient access.
+
+Return a DELETE error for a general delete failure.
+
+=cut
+
sub del_stepkid {
my ($self, $req, $article, $articles) = @_;
$req->check_csrf("admin_remove_stepkid")
or return $self->csrf_error($req, $article, "admin_del_stepkid", "Delete Stepkid");
$req->user_can(edit_stepkid_delete => $article)
- or return $self->edit_form($req, $article, $articles,
- "You don't have access to delete stepchildren from this article");
+ or return $self->_service_error($req, $article, $articles,
+ "You don't have access to delete stepchildren from this article", {}, "ACCESS");
my $cgi = $req->cgi;
- require 'BSE/Admin/StepParents.pm';
- eval {
- my $childId = $cgi->param('stepkid');
- defined $childId
- or die "No stepkid supplied to add_stepkid";
+
+ my %errors;
+ my $childId = $cgi->param('stepkid');
+ defined $childId
+ or $errors{stepkid} = "No stepkid supplied to add_stepkid";
+ unless ($errors{stepkid}) {
$childId =~ /^\d+$/
- or die "Invalid stepkid supplied to add_stepkid";
- my $child = $articles->getByPkey($childId)
- or die "Article $childId not found";
+ or $errors{stepkid} = "Invalid stepkid supplied to add_stepkid";
+ }
+ my $child;
+ unless ($errors{stepkid}) {
+ $child = $articles->getByPkey($childId)
+ or $errors{stepkid} = "Article $childId not found";
+ }
+ keys %errors
+ and return $self->_service_error
+ ($req, $article, $articles, $errors{stepkid}, \%errors, "FIELD");
- $req->user_can(edit_stepparent_delete => $child)
- or die "You cannot remove stepparents from that article\n";
+ $req->user_can(edit_stepparent_delete => $child)
+ or return _service_error($req, $article, $article, "You cannot remove stepparents from that article", {}, "ACCESS");
+
+ require BSE::Admin::StepParents;
+ eval {
BSE::Admin::StepParents->del($article, $child);
};
if ($@) {
- return $self->edit_form($req, $article, $articles, $@);
+ return $self->_service_error($req, $article, $articles, $@, {}, "DELETE");
}
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
- return $self->refresh($article, $cgi, 'step', 'Stepchild deleted');
+ if ($req->is_ajax) {
+ return $req->json_content(success => 1);
+ }
+ else {
+ return $self->refresh($article, $cgi, 'step', 'Stepchild deleted');
+ }
}
sub save_stepkids {
my $cgi = $req->cgi;
require 'BSE/Admin/StepParents.pm';
- my @stepcats = OtherParents->getBy(parentId=>$article->{id});
+ my @stepcats = BSE::TB::OtherParents->getBy(parentId=>$article->{id});
my %stepcats = map { $_->{parentId}, $_ } @stepcats;
my %datedefs = ( release => '2000-01-01', expire=>'2999-12-31' );
for my $stepcat (@stepcats) {
};
$@ and return $self->refresh($article, $cgi, '', $@);
}
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
return $self->refresh($article, $cgi, 'step', 'Stepchild information saved');
}
-sub add_stepparent {
- my ($self, $req, $article, $articles) = @_;
+=item a_restepkid
- $req->check_csrf("admin_add_stepparent")
- or return $self->csrf_error($req, $article, "admin_add_stepparent", "Add Stepparent");
+Moves a stepkid from one parent to another, and sets the order within
+that new stepparent.
- $req->user_can(edit_stepparent_add => $article)
- or return $self->edit_form($req, $article, $articles,
- "You don't have access to add stepparents to this article");
+Parameters:
- my $cgi = $req->cgi;
- require 'BSE/Admin/StepParents.pm';
+=over
+
+=item *
+
+id - id of the step kid to move (required)
+
+=item *
+
+parentid - id of the parent in the stepkid relationship (required)
+
+=item *
+
+newparentid - the new parent for the stepkid relationship (optional)
+
+=item *
+
+_after - id of the allkid under newparentid (or parentid if
+newparentid isn't supplied) to place the stepkid after (0 to place at
+the start)
+
+=back
+
+Errors:
+
+=over
+
+=item *
+
+NOPARENTID - parentid parameter not supplied
+
+=item *
+
+BADPARENTID - non-numeric parentid supplied
+
+=item *
+
+NOTFOUND - no stepkid relationship from parentid was found
+
+=item *
+
+BADNEWPARENT - newparentid is non-numeric
+
+=item *
+
+UNKNOWNNEWPARENT - no article id newparentid found
+
+=item *
+
+NEWPARENTDUP - there's already a stepkid relationship between
+newparentid and id.
+
+=back
+
+=cut
+
+sub req_restepkid {
+ my ($self, $req, $article, $articles) = @_;
+
+ # first, identify the stepkid link
+ my $cgi = $req->cgi;
+ require BSE::TB::OtherParents;
+ my $parentid = $cgi->param("parentid");
+ defined $parentid
+ or return $self->_service_error($req, $article, $articles, "Missing parentid", {}, "NOPARENTID");
+ $parentid =~ /^\d+$/
+ or return $self->_service_error($req, $article, $articles, "Invalid parentid", {}, "BADPARENTID");
+
+ my ($step) = BSE::TB::OtherParents->getBy(parentId => $parentid, childId => $article->id)
+ or return $self->_service_error($req, $article, $articles, "Unknown relationship", {}, "NOTFOUND");
+
+ my $newparentid = $cgi->param("newparentid");
+ if ($newparentid) {
+ $newparentid =~ /^\d+$/
+ or return $self->_service_error($req, $article, $articles, "Bad new parent id", {}, "BADNEWPARENT");
+ my $new_parent = BSE::TB::Articles->getByPkey($newparentid)
+ or return $self->_service_error($req, $article, $articles, "Unknown new parent id", {}, "UNKNOWNNEWPARENT");
+ my $existing =
+ BSE::TB::OtherParents->getBy(parentId=>$newparentid, childId=>$article->id)
+ and return $self->_service_error($req, $article, $articles, "New parent is duplicate", {}, "NEWPARENTDUP");
+
+ $step->{parentId} = $newparentid;
+ $step->save;
+ }
+
+ my $after_id = $cgi->param("_after");
+ if (defined $after_id) {
+ BSE::TB::Articles->reorder_child($step->{parentId}, $article->id, $after_id);
+ }
+
+ if ($req->is_ajax) {
+ return $req->json_content
+ (
+ success => 1,
+ relationshop => $step->data_only,
+ );
+ }
+ else {
+ return $self->refresh($article, $cgi, 'step', "Stepchild moved");
+ }
+}
+
+sub add_stepparent {
+ my ($self, $req, $article, $articles) = @_;
+
+ $req->check_csrf("admin_add_stepparent")
+ or return $self->csrf_error($req, $article, "admin_add_stepparent", "Add Stepparent");
+
+ $req->user_can(edit_stepparent_add => $article)
+ or return $self->edit_form($req, $article, $articles,
+ "You don't have access to add stepparents to this article");
+
+ my $cgi = $req->cgi;
+ require 'BSE/Admin/StepParents.pm';
eval {
my $step_parent_id = $cgi->param('stepparent');
defined($step_parent_id)
};
$@ and return $self->refresh($article, $cgi, 'step', $@);
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
return $self->refresh($article, $cgi, 'stepparents', 'Stepparent added');
};
$@ and return $self->refresh($article, $cgi, 'stepparents', $@);
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
return $self->refresh($article, $cgi, 'stepparents', 'Stepparent deleted');
my $cgi = $req->cgi;
require 'BSE/Admin/StepParents.pm';
- my @stepparents = OtherParents->getBy(childId=>$article->{id});
+ my @stepparents = BSE::TB::OtherParents->getBy(childId=>$article->{id});
my %stepparents = map { $_->{parentId}, $_ } @stepparents;
my %datedefs = ( release => '2000-01-01', expire=>'2999-12-31' );
for my $stepparent (@stepparents) {
$@ and return $self->refresh($article, $cgi, '', $@);
}
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
return $self->refresh($article, $cgi, 'stepparents',
my $cgi = $req->cgi;
my $image_pos = $cgi->param('imagePos');
if ($image_pos
- && $image_pos =~ /^(?:tl|tr|bl|br)$/
+ && $image_pos =~ /^(?:tl|tr|bl|br|xx)$/
&& $image_pos ne $article->{imagePos}) {
$article->{imagePos} = $image_pos;
$article->save;
my $name = $cgi->param("name$id");
if (defined $name && $name ne $image->{name}) {
if ($name eq '') {
- if ($article->{id} > 0) {
- $changes{$id}{name} = '';
- }
- else {
- $errors{"name$id"} = "Identifiers are required for global images";
- }
+ $changes{$id}{name} = '';
}
elsif ($name =~ /^[a-z_]\w*$/i) {
my $msg;
if (defined $filename && length $filename) {
my $in_fh = $cgi->upload("image$id");
if ($in_fh) {
- # work out where to put it
- require DevHelp::FileUpload;
- my $msg;
- my ($image_name, $out_fh) = DevHelp::FileUpload->make_img_filename
- ($image_dir, $filename . '', \$msg);
- if ($image_name) {
- local $/ = \8192;
- my $data;
- while ($data = <$in_fh>) {
- print $out_fh $data;
- }
- close $out_fh;
+ my $basename;
+ my $image_error;
+ my ($width, $height, $type) = $self->_validate_image
+ ($filename, $in_fh, \$basename, \$image_error);
- my $full_filename = "$image_dir/$image_name";
- require Image::Size;
- my ($width, $height, $type) = Image::Size::imgsize($full_filename);
- if ($width) {
- $old_images{$id} =
- {
- image => $image->{image},
- storage => $image->{storage}
- };
- push @new_images, $image_name;
-
- $changes{$id}{image} = $image_name;
- $changes{$id}{storage} = 'local';
- $changes{$id}{src} = "/images/$image_name";
- $changes{$id}{width} = $width;
- $changes{$id}{height} = $height;
- $changes{$id}{ftype} = $self->_image_ftype($type);
+ unless ($type) {
+ $errors{"image$id"} = $image_error;
+ }
+
+ unless ($errors{"image$id"}) {
+ # work out where to put it
+ require DevHelp::FileUpload;
+ my $msg;
+ my ($image_name, $out_fh) = DevHelp::FileUpload->make_img_filename
+ ($image_dir, $basename, \$msg);
+ if ($image_name) {
+ local $/ = \8192;
+ my $data;
+ while ($data = <$in_fh>) {
+ print $out_fh $data;
+ }
+ close $out_fh;
+
+ my $full_filename = "$image_dir/$image_name";
+ if ($width) {
+ $old_images{$id} =
+ {
+ image => $image->{image},
+ storage => $image->{storage}
+ };
+ push @new_images, $image_name;
+
+ $changes{$id}{image} = $image_name;
+ $changes{$id}{storage} = 'local';
+ $changes{$id}{src} = cfg_image_uri() . "/" . $image_name;
+ $changes{$id}{width} = $width;
+ $changes{$id}{height} = $height;
+ $changes{$id}{ftype} = $self->_image_ftype($type);
+ }
}
else {
- $errors{"image$id"} = $type;
+ $errors{"image$id"} = $msg;
}
}
- else {
- $errors{"image$id"} = $msg;
- }
}
else {
# problem uploading
}
if ($changes_found) {
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
}
=cut
sub _service_error {
- my ($self, $req, $article, $articles, $msg, $error, $code) = @_;
+ my ($self, $req, $article, $articles, $msg, $error, $code, $method) = @_;
unless ($article) {
my $mymsg;
$article = $self->_dummy_article($req, $articles, \$mymsg);
$article ||=
{
- map $_ => '', Article->columns
+ map $_ => '', BSE::TB::Article->columns
};
}
my $json_result = $req->json_content($result);
if (!exists $ENV{HTTP_X_REQUESTED_WITH}
- && $ENV{HTTP_X_REQUESTED_WITH} !~ /XMLHttpRequest/) {
+ || $ENV{HTTP_X_REQUESTED_WITH} !~ /XMLHttpRequest/) {
$json_result->{type} = "text/plain";
}
return $json_result;
}
else {
- return $self->edit_form($req, $article, $articles, $msg, $error);
+ $method ||= "edit_form";
+ return $self->$method($req, $article, $articles, $msg, $error);
}
}
};
}
+# FIXME: eliminate this method and call get_ftype directly
sub _image_ftype {
my ($self, $type) = @_;
- if ($type eq 'CWS' || $type eq 'SWF') {
- return "flash";
+ require BSE::TB::Images;
+ return BSE::TB::Images->get_ftype($type);
+}
+
+my %valid_exts =
+ (
+ tiff => "tiff,tif",
+ jpg => "jpeg,jpg",
+ pnm => "pbm,pgm,ppm",
+ );
+
+sub _validate_image {
+ my ($self, $filename, $fh, $rbasename, $error) = @_;
+
+ if ($fh) {
+ if (-z $fh) {
+ $$error = 'Image file is empty';
+ return;
+ }
+ }
+ else {
+ $$error = 'Please enter an image filename';
+ return;
}
+ my $imagename = $filename;
+ $imagename .= ''; # force it into a string
+ (my $basename = $imagename) =~ tr/A-Za-z0-9_./-/cs;
+
+ require BSE::ImageSize;
+
+ my ($width,$height, $type) = BSE::ImageSize::imgsize($fh);
- return "img";
+ unless (defined $width) {
+ $$error = "Unknown image file type";
+ return;
+ }
+
+ my $lctype = lc $type;
+ my @valid_exts = split /,/,
+ BSE::Cfg->single->entry("valid image extensions", $lctype,
+ $valid_exts{$lctype} || $lctype);
+
+ my ($ext) = $basename =~ /\.(\w+)\z/;
+ if (!$ext || !grep $_ eq lc $ext, @valid_exts) {
+ $basename .= ".$valid_exts[0]";
+ }
+ $$rbasename = $basename;
+
+ return ($width, $height, $type);
}
+my $last_display_order = 0;
+
sub do_add_image {
my ($self, $cfg, $article, $image, %opts) = @_;
or $errors->{name} = $workmsg;
}
- if ($image) {
- if (-z $image) {
- $errors->{image} = 'Image file is empty';
- }
- }
- else {
- $errors->{image} = 'Please enter an image filename';
+ my $image_error;
+ my $basename;
+ my ($width, $height, $type) =
+ $self->_validate_image($opts{filename} || $image, $image, \$basename,
+ \$image_error);
+ unless ($width) {
+ $errors->{image} = $image_error;
}
+
keys %$errors
and return;
- my $imagename = $opts{filename} || $image;
- $imagename .= ''; # force it into a string
- my $basename = '';
- $imagename =~ tr/ //d;
- $imagename =~ /([\w.-]+)$/ and $basename = $1;
-
- # create a filename that we hope is unique
- my $filename = time. '_'. $basename;
-
# for the sysopen() constants
use Fcntl;
my $imagedir = cfg_image_dir($cfg);
- # loop until we have a unique filename
- my $counter="";
- $filename = time. '_' . $counter . '_' . $basename
- until sysopen( OUTPUT, "$imagedir/$filename", O_WRONLY| O_CREAT| O_EXCL)
- || ++$counter > 100;
-
- fileno(OUTPUT) or die "Could not open image file: $!";
- # for OSs with special text line endings
- binmode OUTPUT;
+ require DevHelp::FileUpload;
+ my $msg;
+ my ($filename, $fh) =
+ DevHelp::FileUpload->make_img_filename($imagedir, $basename, \$msg);
+ unless ($filename) {
+ $errors->{image} = $msg;
+ return;
+ }
my $buffer;
+ binmode $fh;
+
no strict 'refs';
# read the image in from the browser and output it to our output filehandle
- print OUTPUT $buffer while read $image, $buffer, 1024;
+ print $fh $buffer while read $image, $buffer, 1024;
# close and flush
- close OUTPUT
+ close $fh
or die "Could not close image file $filename: $!";
- use Image::Size;
-
-
- my($width,$height, $type) = imgsize("$imagedir/$filename");
+ my $display_order = time;
+ if ($display_order <= $last_display_order) {
+ $display_order = $last_display_order + 1;
+ }
+ $last_display_order = $display_order;
my $alt = $opts{alt};
defined $alt or $alt = '';
width=>$width,
height => $height,
url => $url,
- displayOrder=>time,
+ displayOrder => $display_order,
name => $imageref,
storage => 'local',
- src => '/images/' . $filename,
+ src => cfg_image_uri() . '/' . $filename,
ftype => $self->_image_ftype($type),
);
require BSE::TB::Images;
"You don't have access to add new images to this article");
my $cgi = $req->cgi;
+
my %errors;
+
+ my $save_tags = $cgi->param("_save_tags");
+ my @tags;
+ if ($save_tags) {
+ @tags = $cgi->param("tags");
+ $self->_validate_tags(\@tags, \%errors);
+ }
+
my $imageobj =
$self->do_add_image
(
$req->cfg,
$article,
- scalar($cgi->param('image')),
+ scalar($cgi->upload('image')),
name => scalar($cgi->param('name')),
alt => scalar($cgi->param('altIn')),
url => scalar($cgi->param('url')),
storage => scalar($cgi->param('storage')),
errors => \%errors,
+ filename => scalar($cgi->param("image")),
);
$imageobj
or return $self->_service_error($req, $article, $articles, undef, \%errors);
+ if ($save_tags) {
+ my $error;
+ $imageobj->set_tags([ grep /\S/, @tags ], \$error);
+ }
+
# typically a soft failure from the storage
$errors{flash}
and $req->flash($errors{flash});
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
if ($cgi->param('_service')) {
sub _image_manager {
my ($self) = @_;
- require BSE::StorageMgr::Images;
-
- return BSE::StorageMgr::Images->new(cfg => $self->cfg);
+ require BSE::TB::Images;
+ return BSE::TB::Images->storage_manager;
}
# remove an image
or return $self->csrf_error($req, $article, "admin_remove_image", "Remove Image");
$req->user_can(edit_images_delete => $article)
- or return $self->edit_form($req, $article, $articles,
- "You don't have access to delete images from this article");
+ or return $self->_service_error($req, $article, $articles,
+ "You don't have access to delete images from this article", {}, "ACCESS");
$imageid or die;
my @images = $self->get_images($article);
- my ($image) = grep $_->{id} == $imageid, @images
- or return $self->show_images($req, $article, $articles, "No such image");
+ my ($image) = grep $_->{id} == $imageid, @images;
+ unless ($image) {
+ if ($req->want_json_response) {
+ return $self->_service_error($req, $article, $articles, "No such image", {}, "NOTFOUND");
+ }
+ else {
+ return $self->show_images($req, $article, $articles, "No such image");
+ }
+ }
if ($image->{storage} ne 'local') {
my $mgr = $self->_image_manager($req->cfg);
}
my $imagedir = cfg_image_dir($req->cfg);
- unlink "$imagedir$image->{image}";
$image->remove;
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
+ if ($req->want_json_response) {
+ return $req->json_content
+ (
+ success => 1,
+ );
+ }
+
return $self->refresh($article, $req->cgi, undef, 'Image removed');
}
$to->save;
$from->save;
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
return $self->refresh($article, $req->cgi, undef, 'Image moved');
$to->save;
$from->save;
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
return $self->refresh($article, $req->cgi, undef, 'Image moved');
my $geometry_id = $cgi->param('g');
defined $geometry_id or $geometry_id = 'editor';
my $geometry = $cfg->entry('thumb geometries', $geometry_id, 'scale(200x200)');
- my $imagedir = $cfg->entry('paths', 'images', $Constants::IMAGEDIR);
+ my $imagedir = cfg_image_dir();
my $error;
($data, $type) = $thumb_obj->thumb_data
}
else {
# grab the nothumb image
- my $uri = $cfg->entry('editor', 'default_thumbnail', '/images/admin/nothumb.png');
- my $filebase = $Constants::CONTENTBASE;
+ my $uri = $cfg->entry('editor', 'default_thumbnail', cfg_dist_image_uri() . '/admin/nothumb.png');
+ my $filebase = $cfg->content_base_path;
if (open IMG, "<$filebase/$uri") {
binmode IMG;
my $data = do { local $/; <IMG> };
}
}
+=item edit_image
+
+Display a form to allow editing an image.
+
+Tags:
+
+=over
+
+=item *
+
+eimage - the image being edited
+
+=item *
+
+normal article edit tags.
+
+=back
+
+Variables:
+
+eimage - the image being edited.
+
+=cut
+
sub req_edit_image {
my ($self, $req, $article, $articles, $errors) = @_;
or return $self->edit_form($req, $article, $articles,
"You don't have access to save image information for this article");
+ $req->set_variable(eimage => $image);
+
my %acts;
%acts =
(
return $req->response('admin/image_edit', \%acts);
}
+=item a_save_image
+
+Save changes to an image.
+
+Parameters:
+
+=over
+
+=item *
+
+id - article id
+
+=item *
+
+image_id - image id
+
+=item *
+
+alt, url, name - text fields to update
+
+=item *
+
+image - replacement image data (if any)
+
+=back
+
+=cut
+
sub req_save_image {
my ($self, $req, $article, $articles) = @_;
my @images = $self->get_images($article);
my ($image) = grep $_->{id} == $id, @images
- or return $self->edit_form($req, $article, $articles,
- "No such image");
+ or return $self->_service_error($req, $article, $articles, "No such image",
+ {}, "NOTFOUND");
$req->user_can(edit_images_save => $article)
- or return $self->edit_form($req, $article, $articles,
- "You don't have access to save image information for this article");
+ or return $self->_service_error($req, $article, $articles,
+ "You don't have access to save image information for this article", {}, "ACCESS");
my $image_dir = cfg_image_dir($req->cfg);
}
}
else {
- if ($article->{id} == -1) {
- $errors{name} = "Identifiers are required for global images";
- }
- else {
- $image->{name} = '';
- }
+ $image->{name} = '';
}
}
my $filename = $cgi->param('image');
if (defined $filename && length $filename) {
my $in_fh = $cgi->upload('image');
if ($in_fh) {
- require DevHelp::FileUpload;
- my $msg;
- my ($image_name, $out_fh) = DevHelp::FileUpload->make_img_filename
- ($image_dir, $filename . '', \$msg);
- if ($image_name) {
- {
- local $/ = \8192;
- my $data;
- while ($data = <$in_fh>) {
- print $out_fh $data;
+ my $basename;
+ my $image_error;
+ my ($width, $height, $type) = $self->_validate_image
+ ($filename, $in_fh, \$basename, \$image_error);
+ if ($type) {
+ require DevHelp::FileUpload;
+ my $msg;
+ my ($image_name, $out_fh) = DevHelp::FileUpload->make_img_filename
+ ($image_dir, $basename, \$msg);
+ if ($image_name) {
+ {
+ local $/ = \8192;
+ my $data;
+ while ($data = <$in_fh>) {
+ print $out_fh $data;
+ }
+ close $out_fh;
}
- close $out_fh;
- }
- my $full_filename = "$image_dir/$image_name";
- require Image::Size;
- my ($width, $height, $type) = Image::Size::imgsize($full_filename);
- if ($width) {
+ my $full_filename = "$image_dir/$image_name";
$delete_file = $image->{image};
$image->{image} = $image_name;
$image->{width} = $width;
$image->{height} = $height;
$image->{storage} = 'local'; # not on the remote store yet
- $image->{src} = '/images/' . $image_name;
+ $image->{src} = cfg_image_uri() . '/' . $image_name;
$image->{ftype} = $self->_image_ftype($type);
}
else {
- $errors{image} = $type;
+ $errors{image} = $msg;
}
}
else {
- $errors{image} = $msg;
+ $errors{image} = $image_error;
}
}
else {
$errors{image} = "No image file received";
}
}
- keys %errors
- and return $self->req_edit_image($req, $article, $articles, \%errors);
+ my $save_tags = $cgi->param("_save_tags");
+ my @tags;
+ if ($save_tags) {
+ @tags = $cgi->param("tags");
+ $self->_validate_tags(\@tags, \%errors);
+ }
+ if (keys %errors) {
+ if ($req->want_json_response) {
+ return $self->_service_error($req, $article, $articles, undef,
+ \%errors, "FIELD");
+ }
+ else {
+ return $self->req_edit_image($req, $article, $articles, \%errors);
+ }
+ }
my $new_storage = $cgi->param('storage');
defined $new_storage or $new_storage = $image->{storage};
$image->save;
+ if ($save_tags) {
+ my $error;
+ $image->set_tags([ grep /\S/, @tags ], \$error);
+ }
my $mgr = $self->_image_manager($req->cfg);
if ($delete_file) {
if ($old_storage ne 'local') {
$@ and $req->flash("There was a problem removing if from the old storage: $@");
}
+ if ($req->want_json_response) {
+ return $req->json_content
+ (
+ success => 1,
+ image => $self->_image_data($req->cfg, $image),
+ );
+ }
+
return $self->refresh($article, $cgi);
}
+=item a_order_images
+
+Change the order of images for an article (or global images).
+
+Ajax only.
+
+=over
+
+=item *
+
+id - id of the article to change the image order for (-1 for global
+images)
+
+=item *
+
+order - comma-separated list of image ids in the new order.
+
+=back
+
+=cut
+
+sub req_order_images {
+ my ($self, $req, $article, $articles) = @_;
+
+ $req->is_ajax
+ or return $self->_service_error($req, $article, $articles, "The function only permitted from Ajax", {}, "AJAXONLY");
+
+ my $order = $req->cgi->param("order");
+ defined $order
+ or return $self->_service_error($req, $article, $articles, "order not supplied", {}, "NOORDER");
+ $order =~ /^\d+(,\d+)*$/
+ or return $self->_service_error($req, $article, $articles, "order not supplied", {}, "BADORDER");
+
+ my @order = split /,/, $order;
+
+ my @images = $article->set_image_order(\@order);
+
+ return $req->json_content
+ (
+ success => 1,
+ images =>
+ [
+ map $self->_image_data($req->cfg, $_), @images
+ ],
+ );
+}
+
sub get_article {
my ($self, $articles, $article) = @_;
$req->check_csrf("admin_add_file")
or return $self->csrf_error($req, $article, "admin_add_file", "Add File");
$req->user_can(edit_files_add => $article)
- or return $self->edit_form($req, $article, $articles,
+ or return $self->_service_error($req, $article, $articles,
"You don't have access to add files to this article");
my %file;
section => $article->{id} == -1 ? 'Global File Validation' : 'Article File Validation');
# build a filename
- my $file = $cgi->param('file');
+ my $file = $cgi->upload('file');
+ my $filename = $cgi->param("file");
unless ($file) {
$errors{file} = 'Please enter a filename';
}
$file{category} ||= '';
defined $file{name} or $file{name} = '';
- if ($article->{id} == -1 && $file{name} eq '') {
- $errors{name} = 'Identifier is required for global files';
- }
if (!$errors{name} && length $file{name} && $file{name} !~/^\w+$/) {
$errors{name} = "Identifier must be a single word";
}
}
keys %errors
- and return $self->edit_form($req, $article, $articles, undef, \%errors);
+ and return $self->_service_error($req, $article, $articles, undef, \%errors);
my $basename = '';
- my $workfile = $file;
+ my $workfile = $filename;
$workfile =~ s![^\w.:/\\-]+!_!g;
$workfile =~ tr/_/_/s;
$workfile =~ /([ \w.-]+)$/ and $basename = $1;
};
$fileobj
- or return $self->edit_form($req, $article, $articles, $@);
+ or return $self->_service_error($req, $article, $articles, $@);
- $req->flash("New file added");
+ unless ($req->is_ajax) {
+ $req->flash("New file added");
+ }
+ my $json =
+ {
+ success => 1,
+ file => $fileobj->data_only,
+ warnings => [],
+ };
my $storage = $cgi->param("storage") || "";
eval {
my $msg;
$article->apply_storage($self->cfg, $fileobj, $storage, \$msg);
- $msg and $req->flash($msg);
+ if ($msg) {
+ if ($req->is_ajax) {
+ push @{$json->{warnings}}, $msg;
+ }
+ else {
+ $req->flash_error($msg);
+ }
+ }
};
- $@
- and $req->flash($@);
-
-# my $downloadPath = $self->{cfg}->entryVar('paths', 'downloads');
-
-
-# unless ($file{contentType}) {
-# unless ($file =~ /\.([^.]+)$/) {
-# $file{contentType} = "application/octet-stream";
-# }
-# unless ($file{contentType}) {
-# $file{contentType} = content_type($self->cfg, $file);
-# }
-# }
-
-
-# # if the user supplies a really long filename, it can overflow the
-# # filename field
-
-# my $work_filename = $basename;
-# if (length $work_filename > 60) {
-# $work_filename = substr($work_filename, -60);
-# }
-
-# my $filename = time. '_'. $work_filename;
-
-# # for the sysopen() constants
-# use Fcntl;
-
-# # loop until we have a unique filename
-# my $counter="";
-# $filename = time. '_' . $counter . '_' . $work_filename
-# until sysopen( OUTPUT, "$downloadPath/$filename",
-# O_WRONLY| O_CREAT| O_EXCL)
-# || ++$counter > 100;
-
-# fileno(OUTPUT) or die "Could not open file: $!";
-
-# # for OSs with special text line endings
-# binmode OUTPUT;
-
-# my $buffer;
-
-# no strict 'refs';
-
-# # read the image in from the browser and output it to our output filehandle
-# print OUTPUT $buffer while read $file, $buffer, 8192;
-
-# # close and flush
-# close OUTPUT
-# or die "Could not close file $filename: $!";
-
-# use BSE::Util::SQL qw/now_datetime/;
-# $file{filename} = $filename;
-# $file{displayName} = $basename;
-# $file{sizeInBytes} = -s $file;
-# $file{displayOrder} = time;
-# $file{whenUploaded} = now_datetime();
-# $file{storage} = 'local';
-# $file{src} = '';
-# $file{file_handler} = "";
-
-# require BSE::TB::ArticleFiles;
-# my $fileobj = BSE::TB::ArticleFiles->add(@file{@cols});
-
-# my $storage = $cgi->param('storage');
-# defined $storage or $storage = 'local';
-# my $file_manager = $self->_file_manager($req->cfg);
-
-# local $SIG{__DIE__};
-# eval {
-# my $src;
-# $storage = $self->_select_filestore($req, $file_manager, $storage, $fileobj);
-# $src = $file_manager->store($filename, $storage, $fileobj);
-
-# if ($src) {
-# $fileobj->{src} = $src;
-# $fileobj->{storage} = $storage;
-# $fileobj->save;
-# }
-# };
-# if ($@) {
-# $req->flash($@);
-# }
-
-# $fileobj->set_handler($req->cfg);
-# $fileobj->save;
+ if ($@) {
+ if ($req->is_ajax) {
+ push @{$json->{warnings}}, $@;
+ }
+ else {
+ $req->flash_error($@);
+ }
+ }
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
- $self->_refresh_filelist($req, $article);
+ if ($req->is_ajax) {
+ return $req->json_content($json);
+ }
+ else {
+ $self->_refresh_filelist($req, $article);
+ }
}
sub fileswap {
}
}
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
$self->refresh($article, $req->cgi, undef, 'File moved');
}
}
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
$self->_refresh_filelist($req, $article, 'File deleted');
my @old_files;
my @new_files;
my %store_anyway;
+ my $change_count = 0;
my @content_changed;
for my $file (@files) {
my $id = $file->{id};
+ my $orig = $file->data_only;
my $desc = $cgi->param("description_$id");
defined $desc and $file->{description} = $desc;
my $type = $cgi->param("contentType_$id");
$errors{"name_$id"} = "Invalid file identifier $name";
}
}
- elsif ($article->{id} == -1) {
- $errors{"name_$id"} = "Identifier is required for global files";
- }
}
else {
push @{$names{$file->{name}}}, $id
$file->{filename} = $file_name;
$file->{storage} = 'local';
$file->{sizeInBytes} = -s $full_name;
- $file->{whenUploaded} = now_datetime();
+ $file->{whenUploaded} = now_sqldatetime();
$file->{displayName} = $display_name;
push @content_changed, $file;
}
$errors{"file_$id"} = "Filename too long";
}
}
+
+ my $new = $file->data_only;
+ COLUMN:
+ for my $col ($file->columns) {
+ if ($new->{$col} ne $orig->{$col}) {
+ ++$change_count;
+ last COLUMN;
+ }
+ }
}
for my $name (keys %names) {
if (@{$names{$name}} > 1) {
return $self->edit_form($req, $article, $articles, undef, \%errors);
}
- $req->flash('File information saved');
+ if ($change_count) {
+ $req->flash("msg:bse/admin/edit/file/save/success_count", [ $change_count ]);
+ }
+ else {
+ $req->flash("msg:bse/admin/edit/file/save/success_none");
+ }
my $mgr = $self->_file_manager($self->cfg);
for my $file (@files) {
$file->save;
$file->save;
}
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
$self->_refresh_filelist($req, $article);
my @metafields = $file->metafields($self->cfg);
+ $req->set_variable(file => $file);
+ $req->set_variable(fields => { BSE::TB::ArticleFile->fields });
+
my $it = BSE::Util::Iterate->new;
my $current_meta;
my %acts;
(
$self->low_edit_tags(\%acts, $req, $article, $articles, undef,
$errors),
- efile => [ \&tag_hash, $file ],
+ efile => [ \&tag_object, $file ],
error_img => [ \&tag_error_img, $req->cfg, $errors ],
ifOldChecked =>
[ \&tag_old_checked, $errors, $cgi, $file ],
my $notes = $cgi->param("notes");
defined $notes and $file->{notes} = $notes;
my $name = $cgi->param("name");
+ require BSE::ImageSize;
if (defined $name) {
$file->{name} = $name;
if (length $name) {
$errors{name} = "Invalid file identifier $name";
}
}
- if (!$errors{name} && $article->{id} == -1) {
- length $name
- or $errors{name} = "Identifier is required for global files";
- }
}
- my @meta;
- my @meta_delete;
- my @metafields = grep !$_->ro, $file->metafields($self->cfg);
- my %current_meta = map { $_ => 1 } $file->metanames;
- for my $meta (@metafields) {
- my $name = $meta->name;
- my $cgi_name = "meta_$name";
- if ($cgi->param("delete_$cgi_name")) {
- for my $metaname ($meta->metanames) {
- push @meta_delete, $metaname
- if $current_meta{$metaname};
- }
- }
- else {
- my $new;
- if ($meta->is_text) {
- my ($value) = $cgi->param($cgi_name);
- if (defined $value &&
- ($value =~ /\S/ || $current_meta{$meta->name})) {
- my $error;
- if ($meta->validate(value => $value, error => \$error)) {
- push @meta,
- {
- name => $name,
- value => $value,
- };
- }
- else {
- $errors{$cgi_name} = $error;
- }
- }
- }
- else {
- my $im = $cgi->param($cgi_name);
- my $up = $cgi->upload($cgi_name);
- if (defined $im && $up) {
- my $data = do { local $/; <$up> };
- my ($width, $height, $type) = imgsize(\$data);
-
- if ($width && $height) {
- push @meta,
- (
- {
- name => $meta->data_name,
- value => $data,
- content_type => "image/\L$type",
- },
- {
- name => $meta->width_name,
- value => $width,
- },
- {
- name => $meta->height_name,
- value => $height,
- },
- );
- }
- else {
- $errors{$cgi_name} = $type;
- }
- }
- }
- }
- }
+ require BSE::FileMetaMeta;
+ my $meta = BSE::FileMetaMeta->retrieve($req, $file, \%errors);
if ($cgi->param('save_file_flags')) {
my $download = 0 + defined $cgi->param("download");
$file->{filename} = $file_name;
$file->{sizeInBytes} = -s $full_name;
- $file->{whenUploaded} = now_datetime();
+ $file->{whenUploaded} = now_sqldatetime();
$file->{displayName} = $display_name;
$file->{storage} = 'local';
}
$file->set_handler($self->cfg);
$file->save;
- $req->flash('File information saved');
+ $req->flash("msg:bse/admin/edit/file/save/success", [ $file->displayName ]);
my $mgr = $self->_file_manager($self->cfg);
my $storage = $cgi->param('storage');
and $req->flash("Could not move $file->{displayName} to $storage: $@");
}
- for my $meta_delete (@meta_delete, map $_->{name}, @meta) {
- $file->delete_meta_by_name($meta_delete);
- }
- for my $meta (@meta) {
- $file->add_meta(%$meta, appdata => 1);
- }
+ BSE::FileMetaMeta->save($file, $meta);
# remove the replaced files
if (my ($old_name, $old_storage) = @old_file) {
unlink "$download_path/$old_name";
}
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
$self->_refresh_filelist($req, $article);
$$rcode = "CHILDREN";
return;
}
- if (grep $_ == $article->{id}, @Constants::NO_DELETE) {
+ if (grep($_ == $article->{id}, @Constants::NO_DELETE)
+ || $req->cfg->entry("undeletable articles", $article->{id})) {
$$rmsg = "Sorry, these pages are essential to the site structure - they cannot be deleted";
$$rcode = "ESSENTIAL";
return;
return $self->_service_error($req, $article, $articles, $why_not, {}, $code);
}
- my $id = $article->id;
+ my $data = $article->data_only;
my $parentid = $article->{parentid};
$article->remove($req->cfg);
return $req->json_content
(
success => 1,
- article_id => $id,
+ article_id => $data->{id},
);
}
my $url = $req->cgi->param('r');
unless ($url) {
- my $urlbase = admin_base_url($req->cfg);
- $url = "$urlbase$ENV{SCRIPT_NAME}?id=$parentid";
- $url .= "&message=Article+deleted";
+ $url = $req->cfg->admin_url("add", { id => $parentid });
}
+
+ $req->flash_notice("msg:bse/admin/edit/remove", [ $data ]);
+
return BSE::Template->get_refresh($url, $self->{cfg});
}
$article->{listed} = 1;
$article->save;
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
}
return $self->refresh($article, $req->cgi, undef, 'Article unhidden');
$article->{listed} = 0;
$article->save;
- use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
}
my $r = $req->cgi->param('r');
linkAlias => '',
author => '',
summary => '',
+ category => '',
);
sub default_value {
if ($col eq 'threshold') {
my $parent = defined $article->{parentid} && $article->{parentid} != -1
- && Articles->getByPkey($article->{parentid});
+ && BSE::TB::Articles->getByPkey($article->{parentid});
$parent and return $parent->{threshold};
if ($col eq 'summaryLength') {
my $parent = defined $article->{parentid} && $article->{parentid} != -1
- && Articles->getByPkey($article->{parentid});
+ && BSE::TB::Articles->getByPkey($article->{parentid});
$parent and return $parent->{summaryLength};
if ($Constants::AUTO_GENERATE) {
require Util;
- Util::generate_article($articles, $article);
+ generate_article($articles, $article);
for my $regen_id (@extra_regen) {
my $regen = $articles->getByPkey($regen_id);
- Util::generate_low($articles, $regen, $self->{cfg});
+ BSE::Regen::generate_low($articles, $regen, $self->{cfg});
}
}
if ($Constants::AUTO_GENERATE) {
require Util;
- Util::generate_article($articles, $article);
+ generate_article($articles, $article);
for my $regen_id (@extra_regen) {
my $regen = $articles->getByPkey($regen_id);
- Util::generate_low($articles, $regen, $self->{cfg});
+ BSE::Regen::generate_low($articles, $regen, $self->{cfg});
}
}
my $msg = $req->csrf_error;
$errors{_csrfp} = $msg;
my $mymsg;
- $article ||= $self->_dummy_article($req, 'Articles', \$mymsg);
+ $article ||= $self->_dummy_article($req, 'BSE::TB::Articles', \$mymsg);
unless ($article) {
require BSE::Edit::Site;
my $site = BSE::Edit::Site->new(cfg=>$req->cfg, db=> BSE::DB->single);
- return $site->edit_sections($req, 'Articles', $mymsg);
+ return $site->edit_sections($req, 'BSE::TB::Articles', $mymsg);
}
- return $self->_service_error($req, $article, 'Articles', $msg, \%errors);
+ return $self->_service_error($req, $article, 'BSE::TB::Articles', $msg, \%errors);
}
=item a_csrp
"Only usable from Ajax", undef, "NOTAJAX");
$ENV{REQUEST_METHOD} eq 'POST'
- or return $self->_service_error($req, $article, "Articles",
+ or return $self->_service_error($req, $article, "BSE::TB::Articles",
"POST required for this action", {}, "NOTPOST");
my %errors;
if (--$depth > 0) {
for my $kid (@kids) {
$kid->{children} = [ _article_kid_summary($kid->{id}, $depth) ];
- $kid->{allkids} = [ Articles->allkid_summary($kid->{id}) ];
+ $kid->{allkids} = [ BSE::TB::Articles->allkid_summary($kid->{id}) ];
}
}
[
_article_kid_summary($article->id, $depth),
],
+ allkids =>
+ [
+ BSE::TB::Articles->allkid_summary($article->id)
+ ],
);
}