1 package BSE::Edit::Article;
3 use base qw(BSE::Edit::Base);
4 use BSE::Util::Tags qw(tag_error_img);
5 use BSE::Util::SQL qw(now_sqldate now_sqldatetime);
6 use BSE::Util::Valid qw/valid_date/;
8 use DevHelp::HTML qw(:default popup_menu);
10 use BSE::CfgInfo qw(custom_class admin_base_url cfg_image_dir);
11 use BSE::Util::Iterate;
13 sub article_dispatch {
14 my ($self, $req, $article, $articles) = @_;
16 BSE::Permissions->check_logon($req)
17 or return BSE::Template->get_refresh($req->url('logon'), $req->cfg);
21 my %actions = $self->article_actions;
22 for my $check (keys %actions) {
23 if ($cgi->param($check) || $cgi->param("$check.x")) {
30 ($action, @extraargs) = $self->other_article_actions($cgi);
33 my $method = $actions{$action};
34 return $self->$method($req, $article, $articles, @extraargs);
37 sub noarticle_dispatch {
38 my ($self, $req, $articles) = @_;
40 BSE::Permissions->check_logon($req)
41 or return BSE::Template->get_refresh($req->url('logon'), $req->cfg);
45 my %actions = $self->noarticle_actions;
46 for my $check (keys %actions) {
47 if ($cgi->param($check) || $cgi->param("$check.x")) {
52 my $method = $actions{$action};
53 return $self->$method($req, $articles);
63 add_stepkid => 'add_stepkid',
64 del_stepkid => 'del_stepkid',
65 save_stepkids => 'save_stepkids',
66 add_stepparent => 'add_stepparent',
67 del_stepparent => 'del_stepparent',
68 save_stepparents => 'save_stepparents',
69 artimg => 'save_image_changes',
70 addimg => 'add_image',
72 showimages => 'show_images',
73 process => 'save_image_changes',
74 removeimg => 'remove_img',
75 moveimgup => 'move_img_up',
76 moveimgdown => 'move_img_down',
77 filelist => 'filelist',
79 fileswap => 'fileswap',
81 filesave => 'filesave',
84 a_thumb => 'req_thumb',
88 sub other_article_actions {
89 my ($self, $cgi) = @_;
91 for my $param ($cgi->param) {
92 if ($param =~ /^removeimg_(\d+)(\.x)?$/) {
93 return ('removeimg', $1 );
100 sub noarticle_actions {
109 my ($self, $parentid, $articles) = @_;
111 if ($parentid == -1) {
115 title=>'All Sections',
122 return $articles->getByPkey($parentid);
127 my ($object, $args) = @_;
129 my $value = $object->{$args};
130 defined $value or $value = '';
131 if ($value =~ /\cJ/ && $value =~ /\cM/) {
138 my ($object, $args) = @_;
140 my $value = $object->{$args};
141 defined $value or $value = '';
142 if ($value =~ /\cJ/ && $value =~ /\cM/) {
145 escape_html($value, '<>&"');
149 my ($level, $cfg) = @_;
151 escape_html($cfg->entry('level names', $level, 'Article'));
160 sub reparent_updown {
164 sub should_be_catalog {
165 my ($self, $article, $parent, $articles) = @_;
167 if ($article->{parentid} && (!$parent || $parent->{id} != $article->{parentid})) {
168 $parent = $articles->getByPkey($article->{id});
171 my $shopid = $self->{cfg}->entryErr('articles', 'shop');
173 return $article->{parentid} && $parent &&
174 ($article->{parentid} == $shopid ||
175 $parent->{generator} eq 'Generate::Catalog');
178 sub possible_parents {
179 my ($self, $article, $articles, $req) = @_;
184 my $shopid = $self->{cfg}->entryErr('articles', 'shop');
185 my @parents = $articles->getBy('level', $article->{level}-1);
186 @parents = grep { $_->{generator} eq 'Generate::Article'
187 && $_->{id} != $shopid } @parents;
189 # user can only select parent they can add to
190 @parents = grep $req->user_can('edit_add_child', $_), @parents;
192 @values = ( map {$_->{id}} @parents );
193 %labels = ( map { $_->{id} => "$_->{title} ($_->{id})" } @parents );
195 if ($article->{level} == 1 && $req->user_can('edit_add_child')) {
197 $labels{-1} = "No parent - this is a section";
200 if ($article->{id} && $self->reparent_updown($article)) {
201 # we also list the siblings and grandparent (if any)
202 my @siblings = grep $_->{id} != $article->{id} && $_->{id} != $shopid,
203 $articles->getBy(parentid => $article->{parentid});
204 @siblings = grep $req->user_can('edit_add_child', $_), @siblings;
205 push @values, map $_->{id}, @siblings;
206 @labels{map $_->{id}, @siblings} =
207 map { "-- move down a level -- $_->{title} ($_->{id})" } @siblings;
209 if ($article->{parentid} != -1) {
210 my $parent = $articles->getByPkey($article->{parentid});
211 if ($parent->{parentid} != -1) {
212 my $gparent = $articles->getByPkey($parent->{parentid});
213 if ($req->user_can('edit_add_child', $gparent)) {
214 push @values, $gparent->{id};
215 $labels{$gparent->{id}} =
216 "-- move up a level -- $gparent->{title} ($gparent->{id})";
220 if ($req->user_can('edit_add_child')) {
222 $labels{-1} = "-- move up a level -- become a section";
228 return (\@values, \%labels);
232 my ($self, $article, $articles, $cgi, $req, $what) = @_;
234 if ($what eq 'listed') {
235 my @values = qw(0 1);
236 my %labels = ( 0=>"No", 1=>"Yes");
237 if ($article->{level} <= 2) {
238 $labels{2} = "In Sections, but not menu";
242 $labels{2} = "In content, but not menus";
245 return popup_menu(-name=>'listed',
248 -default=>$article->{listed});
251 my ($values, $labels) = $self->possible_parents($article, $articles, $req);
253 if (defined $article->{parentid}) {
254 $html = popup_menu(-name=>'parentid',
257 -default => $article->{parentid},
261 $html = popup_menu(-name=>'parentid',
267 # munge the html - we display a default value, so we need to wrap the
268 # default <select /> around this one
269 $html =~ s!^<select[^>]+>|</select>!!gi;
275 my ($arg, $acts, $funcname, $templater) = @_;
276 my ($func, $args) = split ' ', $arg, 2;
277 return $templater->perform($acts, $func, $args) ? 'checked' : '';
280 sub iter_get_images {
281 my ($self, $article) = @_;
283 $article->{id} or return;
284 $self->get_images($article);
288 my ($article, $articles) = @_;
291 $article->{id} or return;
292 if (UNIVERSAL::isa($article, 'Article')) {
293 @children = $article->children;
295 elsif ($article->{id}) {
296 @children = $articles->children($article->{id});
299 return sort { $b->{displayOrder} <=> $a->{displayOrder} } @children;
302 sub tag_if_have_child_type {
303 my ($level, $cfg) = @_;
305 defined $cfg->entry("level names", $level+1);
309 my ($args, $acts, $isname, $templater) = @_;
311 my ($func, $funcargs) = split ' ', $args, 2;
312 return $templater->perform($acts, $func, $funcargs) ? 'Yes' : 'No';
315 sub default_template {
316 my ($self, $article, $cfg, $templates) = @_;
318 if ($article->{parentid}) {
319 my $template = $cfg->entry("children of $article->{parentid}", "template");
321 if $template && grep $_ eq $template, @$templates;
323 if ($article->{level}) {
324 my $template = $cfg->entry("level $article->{level}", "template");
326 if $template && grep $_ eq $template, @$templates;
328 return $templates->[0];
332 my ($self, $article, $cfg, $cgi) = @_;
334 my @templates = sort $self->templates($article);
336 if ($article->{template} && grep $_ eq $article->{template}, @templates) {
337 $default = $article->{template};
341 $default = $self->default_template($article, $cfg, \@templates);
343 return popup_menu(-name=>'template',
344 -values=>\@templates,
350 my ($self, $article) = @_;
353 my $imagedir = cfg_image_dir($self->{cfg});
354 if (opendir TITLE_IMAGES, "$imagedir/titles") {
356 grep -f "$imagedir/titles/$_" && /\.(gif|jpeg|jpg|png)$/i,
357 readdir TITLE_IMAGES;
358 closedir TITLE_IMAGES;
364 sub tag_title_images {
365 my ($self, $article, $cfg, $cgi) = @_;
367 my @images = $self->title_images($article);
368 my @values = ( '', @images );
369 my %labels = ( '' => 'None', map { $_ => $_ } @images );
371 popup_menu(-name=>'titleImage',
374 -default=>$article->{id} ? $article->{titleImage} : '',
378 sub base_template_dirs {
383 my ($self, $article) = @_;
385 my @dirs = $self->base_template_dirs;
386 if (my $parentid = $article->{parentid}) {
387 my $section = "children of $parentid";
388 if (my $dirs = $self->{cfg}->entry($section, 'template_dirs')) {
389 push @dirs, split /,/, $dirs;
392 if (my $id = $article->{id}) {
393 my $section = "article $id";
394 if (my $dirs = $self->{cfg}->entry($section, 'template_dirs')) {
395 push @dirs, split /,/, $dirs;
398 if ($article->{level}) {
399 push @dirs, $article->{level};
400 my $dirs = $self->{cfg}->entry("level $article->{level}", 'template_dirs');
401 push @dirs, split /,/, $dirs if $dirs;
408 my ($self, $article) = @_;
410 my @dirs = $self->template_dirs($article);
412 my @basedirs = BSE::Template->template_dirs($self->{cfg});
413 for my $basedir (@basedirs) {
414 for my $dir (@dirs) {
415 my $path = File::Spec->catdir($basedir, $dir);
417 if (opendir TEMPLATE_DIR, $path) {
418 push(@templates, sort map "$dir/$_",
419 grep -f "$path/$_" && /\.(tmpl|html)$/i, readdir TEMPLATE_DIR);
420 closedir TEMPLATE_DIR;
426 # eliminate any dups, and order it nicely
428 @templates = sort { lc($a) cmp lc($b) }
429 grep !$seen{$_}++, @templates;
431 return (@templates, $self->extra_templates($article));
434 sub extra_templates {
435 my ($self, $article) = @_;
437 my $basedir = $self->{cfg}->entryVar('paths', 'templates');
439 if (my $id = $article->{id}) {
440 push @templates, 'index.tmpl'
441 if $id == 1 && -f "$basedir/index.html";
442 push @templates, 'index2.tmpl'
443 if $id == 2 && -f "$basedir/index2.html";
444 my $shopid = $self->{cfg}->entryErr('articles', 'shop');
445 push @templates, "shop_sect.tmpl"
446 if $id == $shopid && -f "$basedir/shop_sect.tmpl";
447 my $section = "article $id";
448 my $extras = $self->{cfg}->entry($section, 'extra_templates');
449 push @templates, grep /\.(tmpl|html)$/i, split /,/, $extras
459 return '' unless $article->{id} && $article->{id} != -1;
461 <a href="$ENV{SCRIPT_NAME}?id=$article->{parentid}">Edit parent</a> |
468 return unless $article->{id} && $article->{id} > 0;
472 sub _load_step_kids {
473 my ($article, $step_kids) = @_;
475 my @stepkids = OtherParents->getBy(parentId=>$article->{id}) if $article->{id};
476 %$step_kids = map { $_->{childId} => $_ } @stepkids;
477 $step_kids->{loaded} = 1;
480 sub tag_if_step_kid {
481 my ($article, $allkids, $rallkid_index, $step_kids) = @_;
483 _load_step_kids($article, $step_kids) unless $step_kids->{loaded};
485 my $kid = $allkids->[$$rallkid_index]
487 exists $step_kids->{$kid->{id}};
491 my ($article, $allkids, $rallkid_index, $step_kids, $arg) = @_;
493 _load_step_kids($article, $step_kids) unless $step_kids->{loaded};
495 my $kid = $allkids->[$$rallkid_index]
497 my $step_kid = $step_kids->{$kid->{id}}
500 #print STDERR "found kid (want $arg): ", Dumper($kid), Dumper($step_kid);
501 escape_html($step_kid->{$arg});
504 sub tag_move_stepkid {
505 my ($self, $cgi, $req, $article, $allkids, $rallkids_index, $arg,
506 $acts, $funcname, $templater) = @_;
508 $req->user_can(edit_reorder_children => $article)
511 @$allkids > 1 or return '';
513 my ($img_prefix, $urladd) = DevHelp::Tags->get_parms($arg, $acts, $templater);
514 $img_prefix = '' unless defined $img_prefix;
515 $urladd = '' unless defined $urladd;
517 my $cgi_uri = $self->{cfg}->entry('uri', 'cgi', '/cgi-bin');
518 my $url = $ENV{SCRIPT_NAME} . "?id=$article->{id}";
519 if ($cgi->param('_t')) {
520 $url .= "&_t=".$cgi->param('_t');
525 if ($$rallkids_index < $#$allkids) {
526 $down_url = "$cgi_uri/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids->[$$rallkids_index]{id}&other=$allkids->[$$rallkids_index+1]{id}";
529 if ($$rallkids_index > 0) {
530 $up_url = "$cgi_uri/admin/move.pl?stepparent=$article->{id}&d=swap&id=$allkids->[$$rallkids_index]{id}&other=$allkids->[$$rallkids_index-1]{id}";
533 return make_arrows($req->cfg, $down_url, $up_url, $url, $img_prefix);
536 sub possible_stepkids {
537 my ($req, $article, $articles, $stepkids) = @_;
539 $req->user_can(edit_stepkid_add => $article)
542 my @possible = sort { lc $a->{title} cmp lc $b->{title} }
543 grep !$stepkids->{$_->{id}} && $_->{id} != $article->{id}, $articles->all;
544 if ($req->access_control) {
545 @possible = grep $req->user_can(edit_stepparent_add => $_), @possible;
550 sub tag_possible_stepkids {
551 my ($step_kids, $req, $article, $possstepkids, $articles, $cgi) = @_;
553 _load_step_kids($article, $step_kids) unless $step_kids->{loaded};
554 @$possstepkids = possible_stepkids($req, $article, $articles, $step_kids)
555 unless @$possstepkids;
556 my %labels = map { $_->{id} => "$_->{title} ($_->{id})" } @$possstepkids;
558 popup_menu(-name=>'stepkid',
559 -values=> [ map $_->{id}, @$possstepkids ],
560 -labels => \%labels);
563 sub tag_if_possible_stepkids {
564 my ($step_kids, $req, $article, $possstepkids, $articles, $cgi) = @_;
566 _load_step_kids($article, $step_kids) unless $step_kids->{loaded};
567 @$possstepkids = possible_stepkids($req, $article, $articles, $step_kids)
568 unless @$possstepkids;
573 sub iter_get_stepparents {
576 return unless $article->{id} && $article->{id} > 0;
578 OtherParents->getBy(childId=>$article->{id});
581 sub tag_ifStepParents {
582 my ($args, $acts, $funcname, $templater) = @_;
584 return $templater->perform($acts, 'ifStepparents', '');
587 sub tag_stepparent_targ {
588 my ($article, $targs, $rindex, $arg) = @_;
590 if ($article->{id} && $article->{id} > 0 && !@$targs) {
591 @$targs = $article->step_parents;
593 escape_html($targs->[$$rindex]{$arg});
596 sub tag_move_stepparent {
597 my ($self, $cgi, $req, $article, $stepparents, $rindex, $arg,
598 $acts, $funcname, $templater) = @_;
600 $req->user_can(edit_reorder_stepparents => $article)
603 @$stepparents > 1 or return '';
605 my ($img_prefix, $urladd) = DevHelp::Tags->get_parms($arg, $acts, $templater);
606 $img_prefix = '' unless defined $img_prefix;
607 $urladd = '' unless defined $urladd;
609 my $cgi_uri = $self->{cfg}->entry('uri', 'cgi', '/cgi-bin');
610 my $images_uri = $self->{cfg}->entry('uri', 'images', '/images');
612 my $url = $ENV{SCRIPT_NAME} . "?id=$article->{id}";
613 if ($cgi->param('_t')) {
614 $url .= "&_t=".$cgi->param('_t');
617 $url .= "#stepparents";
618 my $blank = qq!<img src="$images_uri/trans_pixel.gif" width="17" height="13" border="0" align="absbottom" alt="" />!;
620 if ($$rindex < $#$stepparents) {
621 $down_url = "$cgi_uri/admin/move.pl?stepchild=$article->{id}&id=$stepparents->[$$rindex]{parentId}&d=swap&other=$stepparents->[$$rindex+1]{parentId}";
625 $up_url = "$cgi_uri/admin/move.pl?stepchild=$article->{id}&id=$stepparents->[$$rindex]{parentId}&d=swap&other=$stepparents->[$$rindex-1]{parentId}";
628 return make_arrows($req->cfg, $down_url, $up_url, $url, $img_prefix);
631 sub _stepparent_possibles {
632 my ($req, $article, $articles, $targs) = @_;
634 $req->user_can(edit_stepparent_add => $article)
637 @$targs = $article->step_parents unless @$targs;
638 my %targs = map { $_->{id}, 1 } @$targs;
639 my @possibles = grep !$targs{$_->{id}} && $_->{id} != $article->{id},
641 if ($req->access_control) {
642 @possibles = grep $req->user_can(edit_stepkid_add => $_), @possibles;
644 @possibles = sort { lc $a->{title} cmp lc $b->{title} } @possibles;
649 sub tag_if_stepparent_possibles {
650 my ($req, $article, $articles, $targs, $possibles) = @_;
652 if ($article->{id} && $article->{id} > 0 && !@$possibles) {
653 @$possibles = _stepparent_possibles($req, $article, $articles, $targs);
658 sub tag_stepparent_possibles {
659 my ($cgi, $req, $article, $articles, $targs, $possibles) = @_;
661 if ($article->{id} && $article->{id} > 0 && !@$possibles) {
662 @$possibles = _stepparent_possibles($req, $article, $articles, $targs);
664 popup_menu(-name=>'stepparent',
665 -values => [ map $_->{id}, @$possibles ],
666 -labels => { map { $_->{id}, "$_->{title} ($_->{id})" }
673 return unless $article->{id} && $article->{id} > 0;
675 return $article->files;
678 sub tag_edit_parent {
681 return '' unless $article->{id} && $article->{id} != -1;
684 <a href="$ENV{SCRIPT_NAME}?id=$article->{parentid}">Edit parent</a> |
688 sub tag_if_children {
689 my ($args, $acts, $funcname, $templater) = @_;
691 return $templater->perform($acts, 'ifChildren', '');
695 my ($self, $req, $article, $kids, $rindex, $arg,
696 $acts, $funcname, $templater) = @_;
698 $req->user_can('edit_reorder_children', $article)
701 @$kids > 1 or return '';
703 $$rindex >=0 && $$rindex < @$kids
704 or return '** movechild can only be used in the children iterator **';
706 my ($img_prefix, $urladd) = DevHelp::Tags->get_parms($arg, $acts, $templater);
707 $img_prefix = '' unless defined $img_prefix;
708 $urladd = '' unless defined $urladd;
710 my $cgi_uri = $self->{cfg}->entry('uri', 'cgi', '/cgi-bin');
711 my $images_uri = $self->{cfg}->entry('uri', 'images', '/images');
712 my $urlbase = admin_base_url($req->cfg);
713 my $refresh_url = "$urlbase$ENV{SCRIPT_NAME}?id=$article->{id}";
714 my $t = $req->cgi->param('_t');
715 if ($t && $t =~ /^\w+$/) {
716 $refresh_url .= "&_t=$t";
719 $refresh_url .= $urladd;
721 my $id = $kids->[$$rindex]{id};
723 if ($$rindex < $#$kids) {
724 $down_url = "$cgi_uri/admin/move.pl?id=$id&d=down&edit=1&all=1";
728 $up_url = "$cgi_uri/admin/move.pl?id=$id&d=up&edit=1&all=1"
731 return make_arrows($req->cfg, $down_url, $up_url, $refresh_url, $img_prefix);
735 my ($article, $args, $acts, $funcname, $templater) = @_;
736 my ($which, $name) = split / /, $args, 2;
740 && ($gen_class = $templater->perform($acts, $which, 'generator'))) {
741 eval "use $gen_class";
743 my $gen = $gen_class->new(top => $article);
744 my $link = $gen->edit_link($templater->perform($acts, $which, 'id'));
745 return qq!<a href="$link">$name</a>!;
752 my ($req, $article, $rindex, $images, $arg,
753 $acts, $funcname, $templater) = @_;
755 $req->user_can(edit_images_reorder => $article)
758 @$images > 1 or return '';
760 $$rindex >= 0 && $$rindex < @$images
761 or return '** imgmove can only be used in image iterator **';
763 my ($img_prefix, $urladd) = DevHelp::Tags->get_parms($arg, $acts, $templater);
764 $img_prefix = '' unless defined $img_prefix;
765 $urladd = '' unless defined $urladd;
767 my $urlbase = admin_base_url($req->cfg);
768 my $url = "$urlbase$ENV{SCRIPT_NAME}?id=$article->{id}";
769 my $t = $req->cgi->param('_t');
770 if ($t && $t =~ /^\w+$/) {
775 my $image = $images->[$$rindex];
777 if ($$rindex < $#$images) {
778 $down_url = "$ENV{SCRIPT_NAME}?id=$article->{id}&moveimgdown=1&imageid=$image->{id}";
782 $up_url = "$ENV{SCRIPT_NAME}?id=$article->{id}&moveimgup=1&imageid=$image->{id}";
784 return make_arrows($req->cfg, $down_url, $up_url, $url, $img_prefix);
788 my ($self, $req, $article, $files, $rindex, $arg,
789 $acts, $funcname, $templater) = @_;
791 $req->user_can('edit_files_reorder', $article)
794 @$files > 1 or return '';
796 my ($img_prefix, $urladd) = DevHelp::Tags->get_parms($arg, $acts, $templater);
797 $img_prefix = '' unless defined $img_prefix;
798 $urladd = '' unless defined $urladd;
800 $$rindex >= 0 && $$rindex < @$files
801 or return '** movefiles can only be used in the files iterator **';
803 my $urlbase = admin_base_url($req->cfg);
804 my $url = "$urlbase$ENV{SCRIPT_NAME}?id=$article->{id}$urladd";
805 my $t = $req->cgi->param('_t');
806 if ($t && $t =~ /^\w+$/) {
811 if ($$rindex < $#$files) {
812 $down_url = "$ENV{SCRIPT_NAME}?fileswap=1&id=$article->{id}&file1=$files->[$$rindex]{id}&file2=$files->[$$rindex+1]{id}";
816 $up_url = "$ENV{SCRIPT_NAME}?fileswap=1&id=$article->{id}&file1=$files->[$$rindex]{id}&file2=$files->[$$rindex-1]{id}";
819 return make_arrows($req->cfg, $down_url, $up_url, $url, $img_prefix);
823 my ($article, $cgi, $args, $acts, $funcname, $templater) = @_;
825 my ($col, $func, $funcargs) = split ' ', $args, 3;
826 my $value = $cgi->param($col);
827 if (defined $value) {
828 return escape_html($value);
832 return $templater->perform($acts, $func, $funcargs);
835 $value = $article->{$args};
836 defined $value or $value = '';
837 return escape_html($value);
842 sub iter_admin_users {
843 require BSE::TB::AdminUsers;
845 BSE::TB::AdminUsers->all;
848 sub iter_admin_groups {
849 require BSE::TB::AdminGroups;
851 BSE::TB::AdminGroups->all;
854 sub tag_if_field_perm {
855 my ($req, $article, $field) = @_;
857 unless ($field =~ /^\w+$/) {
858 print STDERR "Bad fieldname '$field'\n";
861 if ($article->{id}) {
862 return $req->user_can("edit_field_edit_$field", $article);
865 #print STDERR "adding, always successful\n";
871 my ($self, $req, $article, $args, $acts, $funcname, $templater) = @_;
873 my ($col, $func, $funcargs) = split ' ', $args, 3;
874 if ($article->{id}) {
876 return $templater->perform($acts, $func, $funcargs);
879 my $value = $article->{$args};
880 defined $value or $value = '';
881 return escape_html($value);
885 my $value = $self->default_value($req, $article, $col);
886 defined $value or $value = '';
887 return escape_html($value);
897 sub tag_if_flag_set {
898 my ($article, $arg, $acts, $funcname, $templater) = @_;
900 my @args = DevHelp::Tags->get_parms($arg, $acts, $templater);
903 return index($article->{flags}, $args[0]) >= 0;
907 my ($article, $articles) = @_;
911 defined($temp->{parentid}) or return;
912 while ($temp->{parentid} > 0
913 and my $crumb = $articles->getByPkey($temp->{parentid})) {
914 unshift @crumbs, $crumb;
922 my ($args, $acts, $funcname, $templater) = @_;
924 exists $acts->{$args} or return "** need an article name **";
925 my $generator = $templater->perform($acts, $args, 'generator');
927 $generator =~ /^(?:BSE::)?Generate::(\w+)$/
928 or return "** invalid generator $generator **";
933 sub _get_thumbs_class {
936 $self->{cfg}->entry('editor', 'allow_thumb', 0)
939 my $class = $self->{cfg}->entry('editor', 'thumbs_class')
942 (my $filename = "$class.pm") =~ s!::!/!g;
943 eval { require $filename; };
945 print STDERR "** Error loading thumbs_class $class ($filename): $@\n";
949 eval { $obj = $class->new($self->{cfg}) };
951 print STDERR "** Error creating thumbs objects $class: $@\n";
959 my ($cfg, $thumbs_obj, $current_image, $args) = @_;
961 $thumbs_obj or return '';
963 $$current_image or return '** no current image **';
965 my $imagedir = cfg_image_dir($cfg);
967 my $filename = "$imagedir/$$current_image->{image}";
968 -e $filename or return "** image file missing **";
970 my ($max_width, $max_height, $max_pixels) = split ' ', $args;
971 defined $max_width && $max_width eq '-' and undef $max_width;
972 defined $max_height && $max_height eq '-' and undef $max_height;
973 defined $max_pixels && $max_pixels eq '-' and undef $max_pixels;
975 my ($use_orig, $width, $height) = $thumbs_obj->thumb_dimensions
976 ($filename, $$current_image, $max_width, $max_height, $max_pixels);
981 $alt = $$current_image->{alt};
982 $uri = "/images/$$current_image->{image}";
985 $alt = "thumbnail of ".$$current_image->{alt};
986 $uri = "$ENV{SCRIPT_NAME}?a_thumb=1&id=$$current_image->{articleId}&im=$$current_image->{id}&w=$width&h=$height";
989 # link to the default thumbnail
990 $uri = $cfg->entry('editor', 'default_thumbnail', '/images/admin/nothumb.png');
991 $width = $cfg->entry('editor', 'default_thumbnail_width', 100);
992 $height = $cfg->entry('editor', 'default_thumbnail_height', 100);
993 $alt = $cfg->entry('editor', 'default_thumbnail_alt',
994 "no thumbnail available");
997 $alt = escape_html($alt);
998 $uri = escape_html($uri);
999 return qq!<img src="$uri" width="$width" height="$height" alt="$alt" border="0" />!;
1003 my ($self, $acts, $request, $article, $articles, $msg, $errors) = @_;
1005 my $cgi = $request->cgi;
1006 my $show_full = $cgi->param('f_showfull');
1007 $msg ||= join "\n", map escape_html($_), $cgi->param('message'), $cgi->param('m');
1010 if (keys %$errors && !$msg) {
1011 # try to get the errors in the same order as the table
1012 my @cols = $self->table_object($articles)->rowClass->columns;
1013 my %work = %$errors;
1014 my @out = grep defined, delete @work{@cols};
1016 $msg = join "<br>", @out, values %work;
1019 if ($article->{id}) {
1020 if ($article->{parentid} > 0) {
1021 $parent = $article->parent;
1024 $parent = { title=>"No parent - this is a section", id=>-1 };
1028 $parent = { title=>"How did we get here?", id=>0 };
1030 my $cfg = $self->{cfg};
1031 my $mbcs = $cfg->entry('html', 'mbcs', 0);
1032 my $tag_hash = $mbcs ? \&tag_hash_mbcs : \&tag_hash;
1033 my $thumbs_obj_real = $self->_get_thumbs_class();
1034 my $thumbs_obj = $show_full ? undef : $thumbs_obj_real;
1045 my $stepparent_index;
1046 my @stepparent_targs;
1047 my @stepparentpossibles;
1050 my $it = BSE::Util::Iterate->new;
1053 BSE::Util::Tags->basic($acts, $cgi, $cfg),
1054 BSE::Util::Tags->admin($acts, $cfg),
1055 BSE::Util::Tags->secure($request),
1056 article => [ $tag_hash, $article ],
1057 old => [ \&tag_old, $article, $cgi ],
1058 default => [ \&tag_default, $self, $request, $article ],
1059 articleType => [ \&tag_art_type, $article->{level}, $cfg ],
1060 parentType => [ \&tag_art_type, $article->{level}-1, $cfg ],
1061 ifNew => [ \&tag_if_new, $article ],
1062 list => [ \&tag_list, $self, $article, $articles, $cgi, $request ],
1063 script => $ENV{SCRIPT_NAME},
1064 level => $article->{level},
1065 checked => \&tag_checked,
1067 ([ \&iter_get_images, $self, $article ], 'image', 'images', \@images,
1068 \$image_index, undef, \$current_image),
1069 thumbimage => [ \&tag_thumbimage, $cfg, $thumbs_obj, \$current_image ],
1070 ifThumbs => defined($thumbs_obj),
1071 ifCanThumbs => defined($thumbs_obj_real),
1072 imgmove => [ \&tag_imgmove, $request, $article, \$image_index, \@images ],
1074 DevHelp::Tags->make_iterator2
1075 ([ \&iter_get_kids, $article, $articles ],
1076 'child', 'children', \@children, \$child_index),
1077 ifchildren => \&tag_if_children,
1078 childtype => [ \&tag_art_type, $article->{level}+1, $cfg ],
1079 ifHaveChildType => [ \&tag_if_have_child_type, $article->{level}, $cfg ],
1080 movechild => [ \&tag_movechild, $self, $request, $article, \@children,
1083 templates => [ \&tag_templates, $self, $article, $cfg, $cgi ],
1084 titleImages => [ \&tag_title_images, $self, $article, $cfg, $cgi ],
1085 editParent => [ \&tag_edit_parent, $article ],
1086 DevHelp::Tags->make_iterator2
1087 ([ \&iter_allkids, $article ], 'kid', 'kids', \@allkids, \$allkid_index),
1089 [ \&tag_if_step_kid, $article, \@allkids, \$allkid_index, \%stepkids ],
1090 stepkid => [ \&tag_step_kid, $article, \@allkids, \$allkid_index,
1093 [ \&tag_move_stepkid, $self, $cgi, $request, $article, \@allkids,
1095 possible_stepkids =>
1096 [ \&tag_possible_stepkids, \%stepkids, $request, $article,
1097 \@possstepkids, $articles, $cgi ],
1099 [ \&tag_if_possible_stepkids, \%stepkids, $request, $article,
1100 \@possstepkids, $articles, $cgi ],
1101 DevHelp::Tags->make_iterator2
1102 ( [ \&iter_get_stepparents, $article ], 'stepparent', 'stepparents',
1103 \@stepparents, \$stepparent_index),
1104 ifStepParents => \&tag_ifStepParents,
1106 [ \&tag_stepparent_targ, $article, \@stepparent_targs,
1107 \$stepparent_index ],
1109 [ \&tag_move_stepparent, $self, $cgi, $request, $article, \@stepparents,
1110 \$stepparent_index ],
1111 ifStepparentPossibles =>
1112 [ \&tag_if_stepparent_possibles, $request, $article, $articles,
1113 \@stepparent_targs, \@stepparentpossibles, ],
1114 stepparent_possibles =>
1115 [ \&tag_stepparent_possibles, $cgi, $request, $article, $articles,
1116 \@stepparent_targs, \@stepparentpossibles, ],
1117 DevHelp::Tags->make_iterator2
1118 ([ \&iter_files, $article ], 'file', 'files', \@files, \$file_index ),
1120 [ \&tag_movefiles, $self, $request, $article, \@files, \$file_index ],
1121 DevHelp::Tags->make_iterator2
1122 (\&iter_admin_users, 'iadminuser', 'adminusers'),
1123 DevHelp::Tags->make_iterator2
1124 (\&iter_admin_groups, 'iadmingroup', 'admingroups'),
1125 edit => [ \&tag_edit_link, $article ],
1126 error => [ $tag_hash, $errors ],
1127 error_img => [ \&tag_error_img, $cfg, $errors ],
1128 ifFieldPerm => [ \&tag_if_field_perm, $request, $article ],
1129 parent => [ $tag_hash, $parent ],
1130 DevHelp::Tags->make_iterator2
1131 ([ \&iter_flags, $self ], 'flag', 'flags' ),
1132 ifFlagSet => [ \&tag_if_flag_set, $article ],
1133 DevHelp::Tags->make_iterator2
1134 ([ \&iter_crumbs, $article, $articles ], 'crumb', 'crumbs' ),
1135 typename => \&tag_typename,
1140 my ($self, $article, $cgi) = @_;
1142 my $base = $article->{level};
1143 my $t = $cgi->param('_t');
1144 if ($t && $t =~ /^\w+$/) {
1147 return $self->{cfg}->entry('admin templates', $base,
1148 "admin/edit_$base");
1152 my ($self, $article, $cgi) = @_;
1154 $self->edit_template($article, $cgi);
1158 my ($self, $request, $article, $articles, $msg, $errors) = @_;
1160 my $cgi = $request->cgi;
1162 %acts = $self->low_edit_tags(\%acts, $request, $article, $articles, $msg,
1164 my $template = $article->{id} ?
1165 $self->edit_template($article, $cgi) : $self->add_template($article, $cgi);
1167 return BSE::Template->get_response($template, $request->cfg, \%acts);
1171 my ($self, $request, $article, $articles, $msg, $errors) = @_;
1173 return $self->low_edit_form($request, $article, $articles, $msg, $errors);
1177 my ($self, $req, $articles, $msg, $errors) = @_;
1180 my $cgi = $req->cgi;
1181 my $parentid = $cgi->param('parentid');
1183 if ($parentid =~ /^\d+$/) {
1184 if (my $parent = $self->get_parent($parentid, $articles)) {
1185 $level = $parent->{level}+1;
1191 elsif ($parentid eq "-1") {
1195 unless (defined $level) {
1196 $level = $cgi->param('level');
1197 undef $level unless defined $level && $level =~ /^\d+$/
1198 && $level > 0 && $level < 100;
1199 defined $level or $level = 3;
1203 my @cols = Article->columns;
1204 @article{@cols} = ('') x @cols;
1206 $article{parentid} = $parentid;
1207 $article{level} = $level;
1208 $article{body} = '<maximum of 64Kb>';
1209 $article{listed} = 1;
1210 $article{generator} = $self->generator;
1212 my ($values, $labels) = $self->possible_parents(\%article, $articles, $req);
1214 or return $self->edit_sections($req, $articles,
1215 "You can't add children to any article at that level");
1217 return $self->low_edit_form($req, \%article, $articles, $msg, $errors);
1220 sub generator { 'Generate::Article' }
1225 my $gen = $self->generator;
1227 ($gen =~ /(\w+)$/)[0] || 'Article';
1230 sub _validate_common {
1231 my ($self, $data, $articles, $errors, $article) = @_;
1233 # if (defined $data->{parentid} && $data->{parentid} =~ /^(?:-1|\d+)$/) {
1234 # unless ($data->{parentid} == -1 or
1235 # $articles->getByPkey($data->{parentid})) {
1236 # $errors->{parentid} = "Selected parent article doesn't exist";
1240 # $errors->{parentid} = "You need to select a valid parent";
1242 if (exists $data->{title} && $data->{title} !~ /\S/) {
1243 $errors->{title} = "Please enter a title";
1246 if (exists $data->{template} && $data->{template} =~ /\.\./) {
1247 $errors->{template} = "Please only select templates from the list provided";
1253 my ($self, $data, $articles, $errors) = @_;
1255 $self->_validate_common($data, $articles, $errors);
1256 custom_class($self->{cfg})
1257 ->article_validate($data, undef, $self->typename, $errors);
1259 return !keys %$errors;
1263 my ($self, $article, $data, $articles, $errors) = @_;
1265 $self->_validate_common($data, $articles, $errors, $article);
1266 custom_class($self->{cfg})
1267 ->article_validate($data, $article, $self->typename, $errors);
1269 if (exists $data->{release} && !valid_date($data->{release})) {
1270 $errors->{release} = "Invalid release date";
1273 return !keys %$errors;
1276 sub validate_parent {
1281 my ($self, $req, $data, $articles) = @_;
1283 custom_class($self->{cfg})
1284 ->article_fill_new($data, $self->typename);
1290 my ($self, $article) = @_;
1292 # check the config for the article and any of its ancestors
1293 my $work_article = $article;
1294 my $path = $self->{cfg}->entry('article uris', $work_article->{id});
1296 last if $work_article->{parentid} == -1;
1297 $work_article = $work_article->parent;
1298 $path = $self->{cfg}->entry('article uris', $work_article->{id});
1300 return $path if $path;
1302 $self->default_link_path($article);
1305 sub default_link_path {
1306 my ($self, $article) = @_;
1308 $self->{cfg}->entry('uri', 'articles', '/a');
1312 my ($self, $article) = @_;
1314 if ($article->is_dynamic) {
1315 return "/cgi-bin/page.pl?id=$article->{id}&title=".escape_uri($article->{title});
1318 my $article_uri = $self->link_path($article);
1319 my $link = "$article_uri/$article->{id}.html";
1320 my $link_titles = $self->{cfg}->entryBool('basic', 'link_titles', 0);
1322 (my $extra = lc $article->{title}) =~ tr/a-z0-9/_/sc;
1323 $link .= "/" . $extra . "_html";
1330 my ($self, $req, $articles) = @_;
1332 my $cgi = $req->cgi;
1334 my $table_object = $self->table_object($articles);
1335 my @columns = $table_object->rowClass->columns;
1336 $self->save_thumbnail($cgi, undef, \%data);
1337 for my $name (@columns) {
1338 $data{$name} = $cgi->param($name)
1339 if defined $cgi->param($name);
1341 $data{flags} = join '', sort $cgi->param('flags');
1345 if (!defined $data{parentid} || $data{parentid} eq '') {
1346 $errors{parentid} = "Please select a parent";
1348 elsif ($data{parentid} !~ /^(?:-1|\d+)$/) {
1349 $errors{parentid} = "Invalid parent selection (template bug)";
1351 $self->validate(\%data, $articles, \%errors)
1352 or return $self->add_form($req, $articles, $msg, \%errors);
1355 if ($data{parentid} > 0) {
1356 $parent = $articles->getByPkey($data{parentid}) or die;
1357 $req->user_can('edit_add_child', $parent)
1358 or return $self->add_form($req, $articles,
1359 "You cannot add a child to that article");
1360 for my $name (@columns) {
1361 if (exists $data{$name} &&
1362 !$req->user_can("edit_add_field_$name", $parent)) {
1363 delete $data{$name};
1368 $req->user_can('edit_add_child')
1369 or return $self->add_form($req, $articles,
1370 "You cannot create a top-level article");
1371 for my $name (@columns) {
1372 if (exists $data{$name} &&
1373 !$req->user_can("edit_add_field_$name")) {
1374 delete $data{$name};
1379 $self->validate_parent(\%data, $articles, $parent, \$msg)
1380 or return $self->add_form($req, $articles, $msg);
1382 my $level = $parent ? $parent->{level}+1 : 1;
1383 $data{level} = $level;
1384 $data{displayOrder} = time;
1386 $data{admin} ||= '';
1387 $data{generator} = $self->generator;
1388 $data{lastModified} = now_sqldatetime();
1389 $data{listed} = 1 unless defined $data{listed};
1392 $data{pageTitle} = '' unless defined $data{pageTitle};
1393 my $user = $req->getuser;
1394 $data{createdBy} = $user ? $user->{logon} : '';
1395 $data{lastModifiedBy} = $user ? $user->{logon} : '';
1396 $data{created} = now_sqldatetime();
1397 $data{force_dynamic} = 0;
1398 $data{cached_dynamic} = 0;
1399 $data{inherit_siteuser_rights} = 1;
1401 $self->fill_new_data($req, \%data, $articles);
1402 for my $col (qw(titleImage imagePos template keyword)) {
1404 or $data{$col} = $self->default_value($req, \%data, $col);
1407 for my $col (qw(release expire)) {
1408 $data{$col} = sql_date($data{$col});
1411 # these columns are handled a little differently
1412 for my $col (qw(release expire threshold summaryLength )) {
1414 or $data{$col} = $self->default_value($req, \%data, $col);
1418 my $article = $table_object->add(@data{@columns});
1420 # we now have an id - generate the links
1422 my $cgi_uri = $self->{cfg}->entry('uri', 'cgi', '/cgi-bin');
1423 $article->setAdmin("$cgi_uri/admin/admin.pl?id=$article->{id}");
1424 $article->setLink($self->make_link($article));
1427 use Util 'generate_article';
1428 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
1430 my $r = $cgi->param('r');
1432 $r .= ($r =~ /\?/) ? '&' : '?';
1433 $r .= "id=$article->{id}";
1437 $r = admin_base_url($req->cfg) . $article->{admin};
1439 return BSE::Template->get_refresh($r, $self->{cfg});
1444 my ($self, $req, $article, $data) = @_;
1446 if (exists $data->{body}) {
1447 $data->{body} =~ s/\x0D\x0A/\n/g;
1448 $data->{body} =~ tr/\r/\n/;
1450 for my $col (Article->columns) {
1451 next if $col =~ /^custom/;
1452 $article->{$col} = $data->{$col}
1453 if exists $data->{$col} && $col ne 'id' && $col ne 'parentid';
1455 custom_class($self->{cfg})
1456 ->article_fill_old($article, $data, $self->typename);
1462 my ($self, $req, $article, $articles) = @_;
1464 $req->user_can(edit_save => $article)
1465 or return $self->edit_form($req, $article, $articles,
1466 "You don't have access to save this article");
1468 my $old_dynamic = $article->is_dynamic;
1469 my $cgi = $req->cgi;
1471 for my $name ($article->columns) {
1472 $data{$name} = $cgi->param($name)
1473 if defined($cgi->param($name)) and $name ne 'id' && $name ne 'parentid'
1474 && $req->user_can("edit_field_edit_$name", $article);
1478 # checks editor lastModified against record lastModified
1479 if ($self->{cfg}->entry('editor', 'check_modified')) {
1480 if ($article->{lastModified} ne $cgi->param('lastModified')) {
1481 my $whoModified = '';
1482 my $timeModified = ampm_time($article->{lastModified});
1483 if ($article->{lastModifiedBy}) {
1484 $whoModified = "by '$article->{lastModifiedBy}'";
1486 print STDERR "non-matching lastModified, article not saved\n";
1487 my $msg = "Article changes not saved, this article was modified $whoModified at $timeModified since this editor was loaded";
1488 return $self->edit_form($req, $article, $articles, $msg);
1493 # possibly this needs tighter error checking
1494 $data{flags} = join '', sort $cgi->param('flags')
1495 if $req->user_can("edit_field_edit_flags", $article);
1497 $self->validate_old($article, \%data, $articles, \%errors)
1498 or return $self->edit_form($req, $article, $articles, undef, \%errors);
1499 $self->save_thumbnail($cgi, $article, \%data)
1500 if $req->user_can('edit_field_edit_thumbImage', $article);
1501 $self->fill_old_data($req, $article, \%data);
1502 if (exists $article->{template} &&
1503 $article->{template} =~ m|\.\.|) {
1504 my $msg = "Please only select templates from the list provided";
1505 return $self->edit_form($req, $article, $articles, $msg);
1509 my $newparentid = $cgi->param('parentid');
1510 if ($newparentid && $req->user_can('edit_field_edit_parentid', $article)) {
1511 if ($newparentid == $article->{parentid}) {
1514 elsif ($newparentid != -1) {
1515 print STDERR "Reparenting...\n";
1516 my $newparent = $articles->getByPkey($newparentid);
1518 if ($newparent->{level} != $article->{level}-1) {
1519 # the article cannot become a child of itself or one of it's
1521 if ($article->{id} == $newparentid
1522 || $self->is_descendant($article->{id}, $newparentid, $articles)) {
1523 my $msg = "Cannot become a child of itself or of a descendant";
1524 return $self->edit_form($req, $article, $articles, $msg);
1526 my $shopid = $self->{cfg}->entryErr('articles', 'shop');
1527 if ($self->is_descendant($article->{id}, $shopid, $articles)) {
1528 my $msg = "Cannot become a descendant of the shop";
1529 return $self->edit_form($req, $article, $articles, $msg);
1532 $self->reparent($article, $newparentid, $articles, \$msg)
1533 or return $self->edit_form($req, $article, $articles, $msg);
1536 # stays at the same level, nothing special
1537 $article->{parentid} = $newparentid;
1543 # becoming a section
1545 $self->reparent($article, -1, $articles, \$msg)
1546 or return $self->edit_form($req, $article, $articles, $msg);
1550 $article->{listed} = $cgi->param('listed')
1551 if defined $cgi->param('listed') &&
1552 $req->user_can('edit_field_edit_listed', $article);
1553 $article->{release} = sql_date($cgi->param('release'))
1554 if defined $cgi->param('release') &&
1555 $req->user_can('edit_field_edit_release', $article);
1557 $article->{expire} = sql_date($cgi->param('expire')) || $Constants::D_99
1558 if defined $cgi->param('expire') &&
1559 $req->user_can('edit_field_edit_expire', $article);
1560 $article->{lastModified} = now_sqldatetime();
1561 if ($cgi->param('save_force_dynamic')) {
1562 $article->{force_dynamic} = $cgi->param('force_dynamic') ? 1 : 0;
1565 # this need to go last
1566 $article->update_dynamic($self->{cfg});
1567 if ($article->{link} &&
1568 !$self->{cfg}->entry('protect link', $article->{id})) {
1569 my $article_uri = $self->make_link($article);
1570 $article->setLink($article_uri);
1574 my $user = $req->getuser;
1575 $article->{lastModifiedBy} = $user ? $user->{logon} : '';
1580 # if we changed dynamic status, we need to update it for the kids too
1582 if ($article->is_dynamic != $old_dynamic) {
1583 @extra_regen = $self->update_child_dynamic($article, $articles, $req);
1586 use Util 'generate_article';
1587 if ($Constants::AUTO_GENERATE) {
1588 generate_article($articles, $article);
1589 for my $regen_id (@extra_regen) {
1590 my $regen = $articles->getByPkey($regen_id);
1591 Util::generate_low($articles, $article, $self->{cfg});
1595 return $self->refresh($article, $cgi, undef, 'Article saved');
1598 sub update_child_dynamic {
1599 my ($self, $article, $articles, $req) = @_;
1601 my $cfg = $req->cfg;
1602 my @stack = $article->children;
1605 my $workart = pop @stack;
1606 my $old_dynamic = $workart->is_dynamic; # before update
1607 $workart->update_dynamic($cfg);
1608 if ($old_dynamic != $workart->is_dynamic) {
1610 if ($article->{link} && !$cfg->entry('protect link', $workart->{id})) {
1612 ($editor, $workart) = $self->article_class($workart, $articles, $cfg);
1614 my $uri = $editor->make_link($workart);
1615 $workart->setLink($uri);
1618 # save dynamic cache change and link if that changed
1621 push @stack, $workart->children;
1622 push @regen, $workart->{id};
1630 my ($year, $month, $day);
1633 if (($day, $month, $year) = ($str =~ m!(\d+)/(\d+)/(\d+)!)) {
1634 $year += 2000 if $year < 100;
1636 return sprintf("%04d-%02d-%02d", $year, $month, $day);
1642 # Converts 24hr time to 12hr AM/PM time
1645 my ($hour, $minute, $second, $ampm);
1648 if (($hour, $minute, $second) = ($str =~ m!(\d+):(\d+):(\d+)!)) {
1656 return sprintf("%02d:%02d:%02d $ampm", $hour, $minute, $second);
1663 my ($self, $article, $newparentid, $articles, $rmsg) = @_;
1666 if ($newparentid == -1) {
1670 my $parent = $articles->getByPkey($newparentid);
1672 $$rmsg = "Cannot get new parent article";
1675 $newlevel = $parent->{level} + 1;
1677 # the caller will save this one
1678 $article->{parentid} = $newparentid;
1679 $article->{level} = $newlevel;
1680 $article->{displayOrder} = time;
1682 my @change = ( [ $article->{id}, $newlevel ] );
1684 my $this = shift @change;
1685 my ($art, $level) = @$this;
1687 my @kids = $articles->getBy(parentid=>$art);
1688 push @change, map { [ $_->{id}, $level+1 ] } @kids;
1690 for my $kid (@kids) {
1691 $kid->{level} = $level+1;
1699 # tests if $desc is a descendant of $art
1700 # where both are article ids
1702 my ($self, $art, $desc, $articles) = @_;
1706 my $parent = shift @check;
1707 $parent == $desc and return 1;
1708 my @kids = $articles->getBy(parentid=>$parent);
1709 push @check, map $_->{id}, @kids;
1715 sub save_thumbnail {
1716 my ($self, $cgi, $original, $newdata) = @_;
1718 unless ($original) {
1719 @$newdata{qw/thumbImage thumbWidth thumbHeight/} = ('', 0, 0);
1721 my $imagedir = cfg_image_dir($self->{cfg});
1722 if ($cgi->param('remove_thumb') && $original && $original->{thumbImage}) {
1723 unlink("$imagedir/$original->{thumbImage}");
1724 @$newdata{qw/thumbImage thumbWidth thumbHeight/} = ('', 0, 0);
1726 my $image = $cgi->param('thumbnail');
1727 if ($image && -s $image) {
1728 # where to put it...
1730 $image =~ /([\w.-]+)$/ and $name = $1;
1731 my $filename = time . "_" . $name;
1735 $filename = time . '_' . $counter . '_' . $name
1736 until sysopen( OUTPUT, "$imagedir/$filename",
1737 O_WRONLY| O_CREAT| O_EXCL)
1738 || ++$counter > 100;
1740 fileno(OUTPUT) or die "Could not open image file: $!";
1746 # read the image in from the browser and output it to our
1748 print STDERR "\$image ",ref $image,"\n";
1750 print OUTPUT $buffer while sysread $image, $buffer, 1024;
1753 or die "Could not close image output file: $!";
1757 if ($original && $original->{thumbImage}) {
1758 #unlink("$imagedir/$original->{thumbImage}");
1760 @$newdata{qw/thumbWidth thumbHeight/} = imgsize("$imagedir/$filename");
1761 $newdata->{thumbImage} = $filename;
1766 my ($self, $article) = @_;
1768 my $shopid = $self->{cfg}->entryErr('articles', 'shop');
1769 if ($article && $article->{id} && $article->{id} == $shopid) {
1770 return ( 'BSE::Edit::Catalog' );
1772 return ( 'BSE::Edit::Article' );
1776 my ($self, $req, $article, $articles) = @_;
1778 $req->user_can(edit_stepkid_add => $article)
1779 or return $self->edit_form($req, $article, $articles,
1780 "You don't have access to add step children to this article");
1782 my $cgi = $req->cgi;
1783 require 'BSE/Admin/StepParents.pm';
1785 my $childId = $cgi->param('stepkid');
1787 or die "No stepkid supplied to add_stepkid";
1789 or die "Invalid stepkid supplied to add_stepkid";
1790 my $child = $articles->getByPkey($childId)
1791 or die "Article $childId not found";
1793 $req->user_can(edit_stepparent_add => $child)
1794 or die "You don't have access to add a stepparent to that article\n";
1796 use BSE::Util::Valid qw/valid_date/;
1797 my $release = $cgi->param('release');
1798 valid_date($release) or $release = undef;
1799 my $expire = $cgi->param('expire');
1800 valid_date($expire) or $expire = undef;
1803 BSE::Admin::StepParents->add($article, $child, $release, $expire);
1806 return $self->edit_form($req, $article, $articles, $@);
1809 use Util 'generate_article';
1810 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
1812 return $self->refresh($article, $cgi, 'step', 'Stepchild added');
1816 my ($self, $req, $article, $articles) = @_;
1818 $req->user_can(edit_stepkid_delete => $article)
1819 or return $self->edit_form($req, $article, $articles,
1820 "You don't have access to delete stepchildren from this article");
1822 my $cgi = $req->cgi;
1823 require 'BSE/Admin/StepParents.pm';
1825 my $childId = $cgi->param('stepkid');
1827 or die "No stepkid supplied to add_stepkid";
1829 or die "Invalid stepkid supplied to add_stepkid";
1830 my $child = $articles->getByPkey($childId)
1831 or die "Article $childId not found";
1833 $req->user_can(edit_stepparent_delete => $child)
1834 or die "You cannot remove stepparents from that article\n";
1836 BSE::Admin::StepParents->del($article, $child);
1840 return $self->edit_form($req, $article, $articles, $@);
1842 use Util 'generate_article';
1843 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
1845 return $self->refresh($article, $cgi, 'step', 'Stepchild deleted');
1849 my ($self, $req, $article, $articles) = @_;
1851 $req->user_can(edit_stepkid_save => $article)
1852 or return $self->edit_form($req, $article, $articles,
1853 "No access to save stepkid data for this article");
1855 my $cgi = $req->cgi;
1856 require 'BSE/Admin/StepParents.pm';
1857 my @stepcats = OtherParents->getBy(parentId=>$article->{id});
1858 my %stepcats = map { $_->{parentId}, $_ } @stepcats;
1859 my %datedefs = ( release => '2000-01-01', expire=>'2999-12-31' );
1860 for my $stepcat (@stepcats) {
1861 $req->user_can(edit_stepparent_save => $stepcat->{childId})
1863 for my $name (qw/release expire/) {
1864 my $date = $cgi->param($name.'_'.$stepcat->{childId});
1865 if (defined $date) {
1867 $date = $datedefs{$name};
1869 elsif (valid_date($date)) {
1870 use BSE::Util::SQL qw/date_to_sql/;
1871 $date = date_to_sql($date);
1874 return $self->refresh($article, $cgi, '', "Invalid date '$date'");
1876 $stepcat->{$name} = $date;
1882 $@ and return $self->refresh($article, $cgi, '', $@);
1884 use Util 'generate_article';
1885 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
1887 return $self->refresh($article, $cgi, 'step', 'Stepchild information saved');
1890 sub add_stepparent {
1891 my ($self, $req, $article, $articles) = @_;
1893 $req->user_can(edit_stepparent_add => $article)
1894 or return $self->edit_form($req, $article, $articles,
1895 "You don't have access to add stepparents to this article");
1897 my $cgi = $req->cgi;
1898 require 'BSE/Admin/StepParents.pm';
1900 my $step_parent_id = $cgi->param('stepparent');
1901 defined($step_parent_id)
1902 or die "No stepparent supplied to add_stepparent";
1903 int($step_parent_id) eq $step_parent_id
1904 or die "Invalid stepcat supplied to add_stepcat";
1905 my $step_parent = $articles->getByPkey($step_parent_id)
1906 or die "Parent $step_parent_id not found\n";
1908 $req->user_can(edit_stepkid_add => $step_parent)
1909 or die "You don't have access to add a stepkid to that article\n";
1911 my $release = $cgi->param('release');
1913 or $release = "01/01/2000";
1914 use BSE::Util::Valid qw/valid_date/;
1915 $release eq '' or valid_date($release)
1916 or die "Invalid release date";
1917 my $expire = $cgi->param('expire');
1919 or $expire = '31/12/2999';
1920 $expire eq '' or valid_date($expire)
1921 or die "Invalid expire data";
1924 BSE::Admin::StepParents->add($step_parent, $article, $release, $expire);
1926 $@ and return $self->refresh($article, $cgi, 'step', $@);
1928 use Util 'generate_article';
1929 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
1931 return $self->refresh($article, $cgi, 'stepparents', 'Stepparent added');
1934 sub del_stepparent {
1935 my ($self, $req, $article, $articles) = @_;
1937 $req->user_can(edit_stepparent_delete => $article)
1938 or return $self->edit_form($req, $article, $articles,
1939 "You cannot remove stepparents from that article");
1941 my $cgi = $req->cgi;
1942 require 'BSE/Admin/StepParents.pm';
1943 my $step_parent_id = $cgi->param('stepparent');
1944 defined($step_parent_id)
1945 or return $self->refresh($article, $cgi, 'stepparents',
1946 "No stepparent supplied to add_stepcat");
1947 int($step_parent_id) eq $step_parent_id
1948 or return $self->refresh($article, $cgi, 'stepparents',
1949 "Invalid stepparent supplied to add_stepparent");
1950 my $step_parent = $articles->getByPkey($step_parent_id)
1951 or return $self->refresh($article, $cgi, 'stepparent',
1952 "Stepparent $step_parent_id not found");
1954 $req->user_can(edit_stepkid_delete => $step_parent)
1955 or die "You don't have access to remove the stepkid from that article\n";
1958 BSE::Admin::StepParents->del($step_parent, $article);
1960 $@ and return $self->refresh($article, $cgi, 'stepparents', $@);
1962 use Util 'generate_article';
1963 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
1965 return $self->refresh($article, $cgi, 'stepparents', 'Stepparent deleted');
1968 sub save_stepparents {
1969 my ($self, $req, $article, $articles) = @_;
1971 $req->user_can(edit_stepparent_save => $article)
1972 or return $self->edit_form($req, $article, $articles,
1973 "No access to save stepparent data for this artice");
1975 my $cgi = $req->cgi;
1977 require 'BSE/Admin/StepParents.pm';
1978 my @stepparents = OtherParents->getBy(childId=>$article->{id});
1979 my %stepparents = map { $_->{parentId}, $_ } @stepparents;
1980 my %datedefs = ( release => '2000-01-01', expire=>'2999-12-31' );
1981 for my $stepparent (@stepparents) {
1982 $req->user_can(edit_stepkid_save => $stepparent->{parentId})
1984 for my $name (qw/release expire/) {
1985 my $date = $cgi->param($name.'_'.$stepparent->{parentId});
1986 if (defined $date) {
1988 $date = $datedefs{$name};
1990 elsif (valid_date($date)) {
1991 use BSE::Util::SQL qw/date_to_sql/;
1992 $date = date_to_sql($date);
1995 return $self->refresh($article, $cgi, "Invalid date '$date'");
1997 $stepparent->{$name} = $date;
2001 $stepparent->save();
2003 $@ and return $self->refresh($article, $cgi, '', $@);
2006 use Util 'generate_article';
2007 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2009 return $self->refresh($article, $cgi, 'stepparents',
2010 'Stepparent information saved');
2014 my ($self, $article, $cgi, $name, $message, $extras) = @_;
2016 my $url = $cgi->param('r');
2018 if ($url !~ /[?&](m|message)=/ && $message) {
2019 # add in messages if none in the provided refresh
2020 my @msgs = ref $message ? @$message : $message;
2021 for my $msg (@msgs) {
2022 $url .= "&m=" . CGI::escape($msg);
2027 my $urlbase = admin_base_url($self->{cfg});
2028 $url = "$urlbase$ENV{SCRIPT_NAME}?id=$article->{id}";
2030 my @msgs = ref $message ? @$message : $message;
2031 for my $msg (@msgs) {
2032 $url .= "&m=" . CGI::escape($msg);
2035 if ($cgi->param('_t')) {
2036 $url .= "&_t=".CGI::escape($cgi->param('_t'));
2038 $url .= $extras if defined $extras;
2039 my $cgiextras = $cgi->param('e');
2040 $url .= "#$name" if $name;
2043 return BSE::Template->get_refresh($url, $self->{cfg});
2047 my ($self, $req, $article, $articles, $msg, $errors) = @_;
2050 %acts = $self->low_edit_tags(\%acts, $req, $article, $articles, $msg, $errors);
2051 my $template = 'admin/article_img';
2053 return BSE::Template->get_response($template, $req->cfg, \%acts);
2056 sub save_image_changes {
2057 my ($self, $req, $article, $articles) = @_;
2059 $req->user_can(edit_images_save => $article)
2060 or return $self->edit_form($req, $article, $articles,
2061 "You don't have access to save image information for this article");
2063 my $cgi = $req->cgi;
2064 my $image_pos = $cgi->param('imagePos');
2066 && $image_pos =~ /^(?:tl|tr|bl|br)$/
2067 && $image_pos ne $article->{imagePos}) {
2068 $article->{imagePos} = $image_pos;
2071 my @images = $self->get_images($article);
2074 return $self->refresh($article, $cgi, undef, 'No images to save information for');
2077 my @alt = $cgi->param('alt');
2080 for my $index (0..$#images) {
2081 $index < @alt or last;
2082 $images[$index]{alt} = $alt[$index];
2085 my @urls = $cgi->param('url');
2088 for my $index (0..$#images) {
2089 $index < @urls or next;
2090 $images[$index]{url} = $urls[$index];
2094 my @names = map scalar($cgi->param('name'.$_)), 0..$#images;
2096 # make sure there aren't any dups
2099 for my $name (@names) {
2100 defined $name or $name = '';
2102 if ($name =~ /^[a-z_]\w*$/i) {
2103 if ($used{lc $name}++) {
2104 $errors{"name$index"} = 'Image name must be empty or alphanumeric and unique to the article';
2108 $errors{"name$index"} = 'Image name must be unique to the article';
2111 unless ($errors{"name$index"}) {
2113 $self->validate_image_name($name, \$msg)
2114 or $errors{"name$index"} = $msg;
2121 and return $self->edit_form($req, $article, $articles, undef,
2123 for my $index (0..$#images) {
2124 $images[$index]{name} = $names[$index];
2127 for my $image (@images) {
2132 use Util 'generate_article';
2133 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2135 return $self->refresh($article, $cgi, undef, 'Image information saved');
2139 my ($self, $req, $article, $articles) = @_;
2141 $req->user_can(edit_images_add => $article)
2142 or return $self->edit_form($req, $article, $articles,
2143 "You don't have access to add new images to this article");
2145 my $cgi = $req->cgi;
2149 my $imageref = $cgi->param('name');
2150 if (defined $imageref && $imageref ne '') {
2151 if ($imageref =~ /^[a-z_]\w+$/i) {
2152 # make sure it's unique
2153 my @images = $self->get_images($article);
2154 for my $img (@images) {
2155 if (defined $img->{name} && lc $img->{name} eq lc $imageref) {
2156 $errors{name} = 'Image name must be unique to the article';
2162 $errors{name} = 'Image name must be empty or alphanumeric beginning with an alpha character';
2168 unless ($errors{name}) {
2170 $self->validate_image_name($imageref, \$workmsg)
2171 or $errors{name} = $workmsg;
2174 my $image = $cgi->param('image');
2177 $errors{image} = 'Image file is empty';
2181 #$msg = 'Enter or select the name of an image file on your machine';
2182 $errors{image} = 'Please enter an image filename';
2184 if ($msg || keys %errors) {
2185 return $self->edit_form($req, $article, $articles, $msg, \%errors);
2188 my $imagename = $image;
2189 $imagename .= ''; # force it into a string
2191 $imagename =~ /([\w.-]+)$/ and $basename = $1;
2193 # create a filename that we hope is unique
2194 my $filename = time. '_'. $basename;
2196 # for the sysopen() constants
2199 my $imagedir = cfg_image_dir($req->cfg);
2200 # loop until we have a unique filename
2202 $filename = time. '_' . $counter . '_' . $basename
2203 until sysopen( OUTPUT, "$imagedir/$filename", O_WRONLY| O_CREAT| O_EXCL)
2204 || ++$counter > 100;
2206 fileno(OUTPUT) or die "Could not open image file: $!";
2208 # for OSs with special text line endings
2215 # read the image in from the browser and output it to our output filehandle
2216 print OUTPUT $buffer while read $image, $buffer, 1024;
2220 or die "Could not close image file $filename: $!";
2225 my($width,$height) = imgsize("$imagedir/$filename");
2227 my $alt = $cgi->param('altIn');
2228 defined $alt or $alt = '';
2229 my $url = $cgi->param('url');
2230 defined $url or $url = '';
2233 articleId => $article->{id},
2243 my @cols = Image->columns;
2245 my $imageobj = Images->add(@image{@cols});
2247 use Util 'generate_article';
2248 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2250 return $self->refresh($article, $cgi, undef, 'New image added');
2255 my ($self, $req, $article, $articles, $imageid) = @_;
2257 $req->user_can(edit_images_delete => $article)
2258 or return $self->edit_form($req, $article, $articles,
2259 "You don't have access to delete images from this article");
2263 my @images = $self->get_images($article);
2264 my ($image) = grep $_->{id} == $imageid, @images
2265 or return $self->show_images($req, $article, $articles, "No such image");
2266 my $imagedir = cfg_image_dir($req->cfg);
2267 unlink "$imagedir$image->{image}";
2270 use Util 'generate_article';
2271 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2273 return $self->refresh($article, $req->cgi, undef, 'Image removed');
2277 my ($self, $req, $article, $articles) = @_;
2279 $req->user_can(edit_images_reorder => $article)
2280 or return $self->edit_form($req, $article, $articles,
2281 "You don't have access to reorder images in this article");
2283 my $imageid = $req->cgi->param('imageid');
2284 my @images = $self->get_images($article);
2285 my ($imgindex) = grep $images[$_]{id} == $imageid, 0..$#images
2286 or return $self->edit_form($req, $article, $articles, "No such image");
2288 or return $self->edit_form($req, $article, $articles, "Image is already at the top");
2289 my ($to, $from) = @images[$imgindex-1, $imgindex];
2290 ($to->{displayOrder}, $from->{displayOrder}) =
2291 ($from->{displayOrder}, $to->{displayOrder});
2295 use Util 'generate_article';
2296 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2298 return $self->refresh($article, $req->cgi, undef, 'Image moved');
2302 my ($self, $req, $article, $articles) = @_;
2304 $req->user_can(edit_images_reorder => $article)
2305 or return $self->edit_form($req, $article, $articles,
2306 "You don't have access to reorder images in this article");
2308 my $imageid = $req->cgi->param('imageid');
2309 my @images = $self->get_images($article);
2310 my ($imgindex) = grep $images[$_]{id} == $imageid, 0..$#images
2311 or return $self->edit_form($req, $article, $articles, "No such image");
2312 $imgindex < $#images
2313 or return $self->edit_form($req, $article, $articles, "Image is already at the end");
2314 my ($to, $from) = @images[$imgindex+1, $imgindex];
2315 ($to->{displayOrder}, $from->{displayOrder}) =
2316 ($from->{displayOrder}, $to->{displayOrder});
2320 use Util 'generate_article';
2321 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2323 return $self->refresh($article, $req->cgi, undef, 'Image moved');
2327 my ($self, $req, $article) = @_;
2329 my $cgi = $req->cgi;
2330 my $cfg = $req->cfg;
2331 my $im_id = $cgi->param('im');
2333 if (defined $im_id && $im_id =~ /^\d+$/) {
2334 ($image) = grep $_->{id} == $im_id, $self->get_images($article);
2336 my $thumb_obj = $self->_get_thumbs_class();
2338 if ($image && $thumb_obj) {
2339 my $width = $cgi->param('w');
2340 my $height = $cgi->param('h');
2341 my $pixels = $cgi->param('p');
2342 my $imagedir = $cfg->entry('paths', 'images', $Constants::IMAGEDIR);
2344 ($type, $data) = $thumb_obj->
2345 thumb_data("$imagedir/$image->{image}", $image, $width, $height,
2349 if ($type && $data) {
2356 "Content-Length: ".length($data),
2357 "Cache-Control: max-age=3600",
2362 # grab the nothumb image
2363 my $uri = $cfg->entry('editor', 'default_thumbnail', '/images/admin/nothumb.png');
2364 my $filebase = $Constants::CONTENTBASE;
2365 if (open IMG, "<$filebase/$uri") {
2367 my $data = do { local $/; <IMG> };
2369 my $type = $uri =~ /\.(\w+)$/ ? $1 : 'png';
2372 type => "image/$type",
2374 headers => [ "Content-Length: ".length($data) ],
2381 content => "<html><body>Cannot make thumb or default image</body></html>",
2388 my ($self, $articles, $article) = @_;
2394 my ($self, $articles) = @_;
2419 tsv text/tab-separated-values
2422 vcs text/x-vcalendar
2424 zsh text/x-script.zsh
2435 Z application/x-compress
2436 dcr application/x-director
2437 dir application/x-director
2438 doc application/msword
2439 dxr application/x-director
2440 eps application/postscript
2441 fla application/x-shockwave-flash
2443 hqx application/mac-binhex40
2444 js application/x-javascript
2445 lzh application/x-lzh
2447 pps application/ms-powerpoint
2448 ppt application/ms-powerpoint
2449 ps application/postscript
2451 sit application/x-stuffit
2452 swf application/x-shockwave-flash
2453 tar application/x-tar
2454 tgz application/gzip
2455 xls application/ms-excel
2460 moov video/quicktime
2473 ra audio/x-realaudio
2474 ram audio/x-pn-realaudio
2475 rm audio/vnd.rm-realmedia
2483 sub _refresh_filelist {
2484 my ($self, $req, $article, $msg) = @_;
2486 return $self->refresh($article, $req->cgi, undef, $msg);
2490 my ($self, $req, $article, $articles, $msg, $errors) = @_;
2493 %acts = $self->low_edit_tags(\%acts, $req, $article, $articles, $msg, $errors);
2494 my $template = 'admin/filelist';
2496 return BSE::Template->get_response($template, $req->cfg, \%acts);
2500 my ($self, $req, $article, $articles) = @_;
2502 $req->user_can(edit_files_add => $article)
2503 or return $self->edit_form($req, $article, $articles,
2504 "You don't have access to add files to this article");
2507 my $cgi = $req->cgi;
2508 require ArticleFile;
2509 my @cols = ArticleFile->columns;
2511 for my $col (@cols) {
2512 if (defined $cgi->param($col)) {
2513 $file{$col} = $cgi->param($col);
2517 $file{forSale} = 0 + exists $file{forSale};
2518 $file{articleId} = $article->{id};
2519 $file{download} = 0 + exists $file{download};
2520 $file{requireUser} = 0 + exists $file{requireUser};
2522 my $downloadPath = $self->{cfg}->entryVar('paths', 'downloads');
2525 my $file = $cgi->param('file');
2527 return $self->edit_form($req, $article, $articles,
2528 "Enter or select the name of a file on your machine",
2529 { file => 'Please enter a filename' });
2532 return $self->edit_form($req, $article, $articles,
2534 { file => 'File is empty' });
2537 unless ($file{contentType}) {
2538 unless ($file =~ /\.([^.]+)$/) {
2539 $file{contentType} = "application/octet-stream";
2541 unless ($file{contentType}) {
2543 my $type = $types{$ext};
2545 $type = $self->{cfg}->entry('extensions', $ext)
2546 || $self->{cfg}->entry('extensions', ".$ext")
2547 || "application/octet-stream";
2549 $file{contentType} = $type;
2554 my $workfile = $file;
2555 $workfile =~ s![^\w.:/\\-]+!_!g;
2556 $workfile =~ tr/_/_/s;
2557 $workfile =~ /([ \w.-]+)$/ and $basename = $1;
2558 $basename =~ tr/ /_/;
2560 my $filename = time. '_'. $basename;
2562 # for the sysopen() constants
2565 # loop until we have a unique filename
2567 $filename = time. '_' . $counter . '_' . $basename
2568 until sysopen( OUTPUT, "$downloadPath/$filename",
2569 O_WRONLY| O_CREAT| O_EXCL)
2570 || ++$counter > 100;
2572 fileno(OUTPUT) or die "Could not open file: $!";
2574 # for OSs with special text line endings
2581 # read the image in from the browser and output it to our output filehandle
2582 print OUTPUT $buffer while read $file, $buffer, 8192;
2586 or die "Could not close file $filename: $!";
2588 use BSE::Util::SQL qw/now_datetime/;
2589 $file{filename} = $filename;
2590 $file{displayName} = $basename;
2591 $file{sizeInBytes} = -s $file;
2592 $file{displayOrder} = time;
2593 $file{whenUploaded} = now_datetime();
2595 require ArticleFiles;
2596 my $fileobj = ArticleFiles->add(@file{@cols});
2598 use Util 'generate_article';
2599 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2601 $self->_refresh_filelist($req, $article, 'New file added');
2605 my ($self, $req, $article, $articles) = @_;
2607 $req->user_can('edit_files_reorder', $article)
2608 or return $self->edit_form($req, $article, $articles,
2609 "You don't have access to reorder files in this article");
2611 my $cgi = $req->cgi;
2612 my $id1 = $cgi->param('file1');
2613 my $id2 = $cgi->param('file2');
2616 my @files = $article->files;
2618 my ($file1) = grep $_->{id} == $id1, @files;
2619 my ($file2) = grep $_->{id} == $id2, @files;
2621 if ($file1 && $file2) {
2622 ($file1->{displayOrder}, $file2->{displayOrder})
2623 = ($file2->{displayOrder}, $file1->{displayOrder});
2629 use Util 'generate_article';
2630 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2632 $self->refresh($article, $req->cgi, undef, 'File moved');
2636 my ($self, $req, $article, $articles) = @_;
2638 $req->user_can('edit_files_delete', $article)
2639 or return $self->edit_form($req, $article, $articles,
2640 "You don't have access to delete files from this article");
2642 my $cgi = $req->cgi;
2643 my $fileid = $cgi->param('file');
2645 my @files = $article->files;
2647 my ($file) = grep $_->{id} == $fileid, @files;
2650 my $downloadPath = $req->cfg->entryErr('paths', 'downloads');
2651 my $filename = $downloadPath . "/" . $file->{filename};
2652 my $debug_del = $req->cfg->entryBool('debug', 'file_unlink', 0);
2655 or print STDERR "Error deleting $filename: $!\n";
2664 use Util 'generate_article';
2665 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2667 $self->_refresh_filelist($req, $article, 'File deleted');
2671 my ($self, $req, $article, $articles) = @_;
2673 $req->user_can('edit_files_save', $article)
2674 or return $self->edit_form($req, $article, $articles,
2675 "You don't have access to save file information for this article");
2676 my @files = $article->files;
2678 my $cgi = $req->cgi;
2679 for my $file (@files) {
2680 if (defined $cgi->param("description_$file->{id}")) {
2681 $file->{description} = $cgi->param("description_$file->{id}");
2682 if (my $type = $cgi->param("contentType_$file->{id}")) {
2683 $file->{contentType} = $type;
2685 $file->{download} = 0 + defined $cgi->param("download_$file->{id}");
2686 $file->{forSale} = 0 + defined $cgi->param("forSale_$file->{id}");
2687 $file->{requireUser} = 0 + defined $cgi->param("requireUser_$file->{id}");
2692 use Util 'generate_article';
2693 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2695 $self->_refresh_filelist($req, $article, 'File information saved');
2699 my ($self, $req, $article, $articles, $rmsg) = @_;
2701 unless ($req->user_can('edit_delete_article', $article, $rmsg)) {
2702 $$rmsg ||= "Access denied";
2706 if ($articles->children($article->{id})) {
2707 $$rmsg = "This article has children. You must delete the children first (or change their parents)";
2710 if (grep $_ == $article->{id}, @Constants::NO_DELETE) {
2711 $$rmsg = "Sorry, these pages are essential to the site structure - they cannot be deleted";
2714 if ($article->{id} == $Constants::SHOPID) {
2715 $$rmsg = "Sorry, these pages are essential to the store - they cannot be deleted - you may want to hide the the store instead.";
2723 my ($self, $req, $article, $articles) = @_;
2726 unless ($self->can_remove($req, $article, $articles, \$why_not)) {
2727 return $self->edit_form($req, $article, $articles, $why_not);
2731 my @images = Images->getBy(articleId=>$article->{id});
2732 my $imagedir = $self->{cfg}->entry('paths', 'images', $Constants::IMAGEDIR);
2733 for my $image (@images) {
2734 unlink("$imagedir/$image->{image}");
2738 # remove any step(child|parent) links
2739 require OtherParents;
2740 my @steprels = OtherParents->anylinks($article->{id});
2741 for my $link (@steprels) {
2745 my $parentid = $article->{parentid};
2747 my $url = $req->cgi->param('r');
2749 my $urlbase = admin_base_url($req->cfg);
2750 $url = "$urlbase$ENV{SCRIPT_NAME}?id=$parentid";
2751 $url .= "&message=Article+deleted";
2753 return BSE::Template->get_refresh($url, $self->{cfg});
2757 my ($self, $req, $article, $articles) = @_;
2759 if ($req->user_can(edit_field_edit_listed => $article)
2760 && $req->user_can(edit_save => $article)) {
2761 $article->{listed} = 1;
2764 use Util 'generate_article';
2765 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2767 return $self->refresh($article, $req->cgi, undef, 'Article unhidden');
2771 my ($self, $req, $article, $articles) = @_;
2773 if ($req->user_can(edit_field_edit_listed => $article)
2774 && $req->user_can(edit_save => $article)) {
2775 $article->{listed} = 0;
2778 use Util 'generate_article';
2779 generate_article($articles, $article) if $Constants::AUTO_GENERATE;
2781 my $r = $req->cgi->param('r');
2783 $r = admin_base_url($req->cfg)
2784 . "/cgi-bin/admin/add.pl?id=" . $article->{parentid};
2786 return $self->refresh($article, $req->cgi, undef, 'Article hidden');
2793 expire => $Constants::D_99,
2796 body => '<maximum of 64Kb>',
2800 my ($self, $req, $article, $col) = @_;
2802 if ($article->{parentid}) {
2803 my $section = "children of $article->{parentid}";
2804 my $value = $req->cfg->entry($section, $col);
2805 if (defined $value) {
2809 my $section = "level $article->{level}";
2810 my $value = $req->cfg->entry($section, $col);
2811 defined($value) and return $value;
2813 $value = $self->type_default_value($req, $col);
2814 defined $value and return $value;
2816 exists $defaults{$col} and return $defaults{$col};
2818 $col eq 'release' and return now_sqldate();
2820 if ($col eq 'threshold') {
2821 my $parent = defined $article->{parentid} && $article->{parentid} != -1
2822 && Articles->getByPkey($article->{parentid});
2824 $parent and return $parent->{threshold};
2829 if ($col eq 'summaryLength') {
2830 my $parent = defined $article->{parentid} && $article->{parentid} != -1
2831 && Articles->getByPkey($article->{parentid});
2833 $parent and return $parent->{summaryLength};
2841 sub type_default_value {
2842 my ($self, $req, $col) = @_;
2844 return $req->cfg->entry('article defaults', $col);
2848 return ( 'article flags' );
2854 my $cfg = $self->{cfg};
2856 my @sections = $self->flag_sections;
2858 my %flags = map $cfg->entriesCS($_), reverse @sections;
2859 my @valid = grep /^\w$/, keys %flags;
2861 return map +{ id => $_, desc => $flags{$_} },
2862 sort { lc($flags{$a}) cmp lc($flags{$b}) }@valid;
2866 my ($self, $article) = @_;
2871 sub validate_image_name {
2872 my ($self, $name, $rmsg) = @_;
2874 1; # no extra validation
2881 BSE::Edit::Article - editing functionality for BSE articles
2885 Tony Cook <tony@develop-help.com>