-- more descriptive stuff
notes text not null default '',
+ -- identifier for the file for use with filelink[]
+ name varchar(80) not null default '',
+
primary key (id)
);
sub columns {
return qw/id articleId displayName filename sizeInBytes description
contentType displayOrder forSale download whenUploaded
- requireUser notes/;
+ requireUser notes name/;
}
sub remove {
'select * from other_parents where childId = ? or parentId = ?',
addArticleFile =>
- 'insert into article_files values (null,?,?,?,?,?,?,?,?,?,?,?,?)',
+ 'insert into article_files values (null,?,?,?,?,?,?,?,?,?,?,?,?,?)',
replaceArticleFile =>
- 'replace article_files values (?,?,?,?,?,?,?,?,?,?,?,?,?)',
+ 'replace article_files values (?,?,?,?,?,?,?,?,?,?,?,?,?,?)',
deleteArticleFile => 'delete from article_files where id = ?',
getArticleFileByArticleId =>
'select * from article_files where articleId = ? order by displayOrder desc',
replaceSiteUserGroup => 'replace bse_siteuser_groups values(?,?)',
deleteSiteUserGroup => 'delete from bse_siteuser_groups where id = ?',
getSiteUserGroupByPkey => 'select * from bse_siteuser_groups where id = ?',
+ getSiteUserGroupByName => 'select * from bse_siteuser_groups where name = ?',
siteuserGroupMemberIds => <<SQL,
select siteuser_id as "id"
from bse_siteuser_membership
$file{$col} = $cgi->param($col);
}
}
+
+ my %errors;
$file{forSale} = 0 + exists $file{forSale};
$file{articleId} = $article->{id};
# build a filename
my $file = $cgi->param('file');
unless ($file) {
- return $self->edit_form($req, $article, $articles,
- "Enter or select the name of a file on your machine",
- { file => 'Please enter a filename' });
+ $errors{file} = 'Please enter a filename';
}
- if (-z $file) {
- return $self->edit_form($req, $article, $articles,
- "File is empty",
- { file => 'File is empty' });
+ if ($file && -z $file) {
+ $errors{file} = 'File is empty';
}
unless ($file{contentType}) {
$file{contentType} = $type;
}
}
+
+ defined $file{name} or $file{name} = '';
+ if (length $file{name} && $file{name} !~/^\w+$/) {
+ $errors{name} = "Identifier must be a single word";
+ }
+ if (!$errors{name} && length $file{name}) {
+ my @files = $article->files;
+ if (grep lc $_->{name} eq lc $file{name}, @files) {
+ $errors{name} = "Duplicate file identifier $file{name}";
+ }
+ }
+
+ keys %errors
+ and return $self->edit_form($req, $article, $articles, undef, \%errors);
my $basename = '';
my $workfile = $file;
my @files = $article->files;
my $cgi = $req->cgi;
+ my %seen_name;
for my $file (@files) {
if (defined $cgi->param("description_$file->{id}")) {
$file->{description} = $cgi->param("description_$file->{id}");
- if (my $type = $cgi->param("contentType_$file->{id}")) {
+ if (defined(my $type = $cgi->param("contentType_$file->{id}"))) {
$file->{contentType} = $type;
}
- if (my $notes = $cgi->param("notes_$file->{id}")) {
+ if (defined(my $notes = $cgi->param("notes_$file->{id}"))) {
$file->{notes} = $notes;
}
+ my $name = $cgi->param("name_$file->{id}");
+ defined $name or $name = $file->{name};
+ if (length $name && $name !~ /^\w+$/) {
+ return $self->edit_form($req, $article, $articles,
+ "Invalid file identifier $name");
+ }
+ if (length $name && $seen_name{lc $name}++) {
+ return $self->edit_form($req, $article, $articles,
+ "Duplicate file identifier $name");
+ }
+ $file->{name} = $name;
$file->{download} = 0 + defined $cgi->param("download_$file->{id}");
$file->{forSale} = 0 + defined $cgi->param("forSale_$file->{id}");
$file->{requireUser} = 0 + defined $cgi->param("requireUser_$file->{id}");
- $file->save;
}
}
-
+ for my $file (@files) {
+ $file->save;
+ }
use Util 'generate_article';
generate_article($articles, $article) if $Constants::AUTO_GENERATE;
my $pop_nameid = 'AAAAAA';
sub new {
- my ($class, $gen, $acts, $articles, $abs_urls, $rauto_images, $images, $templater) = @_;
+ my $class = shift;
- my $self = $class->SUPER::new;
+ my (%opts) =
+ (
+ images => [],
+ files => [],
+ abs_urls => 0,
+ acts => {},
+ @_
+ );
- $self->{gen} = $gen;
- $self->{acts} = $acts;
- $self->{articles} = $articles;
- $self->{abs_urls} = $abs_urls;
- $self->{auto_images} = $rauto_images;
- $self->{images} = $images;
- $self->{templater} = $templater;
- #$self->{level} = $level;
+ my $self = $class->SUPER::new;
- my $cfg = $gen->{cfg};
+ $self->{gen} = $opts{gen};
+ $self->{acts} = $opts{acts};
+ $self->{articles} = $opts{articles};
+ $self->{abs_urls} = $opts{abs_urls};
+ my $dummy;
+ $self->{auto_images} = $opts{auto_images} || \$dummy;
+ $self->{images} = $opts{images};
+ $self->{files} = $opts{files};
+ $self->{templater} = $opts{templater};
+
+ my $cfg = $self->{gen}->{cfg};
if ($cfg->entry('html', 'mbcs', 0)) {
$self->{conservative_escape} = 1;
}
return $self->_fix_spanned($link_start, $link_end, $inside);
}
+sub filelink {
+ my ($self, $fileid, $text) = @_;
+
+ my ($file) = grep $_->{name} eq $fileid, @{$self->{files}}
+ or return "** unknown file $fileid **";
+
+ my $title = defined $text ? $text : $file->{displayName};
+ if ($file->{forSale}) {
+ return escape_html($title);
+ }
+ else {
+ my $url = "/cgi-bin/user.pl?download_file=1&file=$file->{id}";
+ return qq!<a href="! . escape_html($url) . qq!">! .
+ escape_html($title) . "</a>";
+ }
+}
+
sub replace {
my ($self, $rpart) = @_;
and return 1;
$$rpart =~ s#formlink\[(\w+)\]# $self->formlink($1, 'formlink', undef) #ige
and return 1;
+ $$rpart =~ s#filelink\[\s*(\w+)\s*\|([^\]\[]+)\]# $self->filelink($1, $2) #ige
+ and return 1;
+ $$rpart =~ s#filelink\[\s*(\w+)\s*\]# $self->filelink($1) #ige
+ and return 1;
$$rpart =~ s#popimage\[([^\[\]]+)\]# $self->popimage($1) #ige
and return 1;
}
}
+sub remove_filelink {
+ my ($self, $fileid, $text) = @_;
+
+ my ($file) = grep $_->{name} eq $fileid, @{$self->{files}}
+ or return "** unknown file $fileid **";
+
+ return defined $text ? $text : $file->{displayName};
+}
+
sub remove {
my ($self, $rpart) = @_;
and return 1;
$$rpart =~ s#popformlink\[(\w+)\]# $self->remove_formlink($1) #ige
and return 1;
+
+ $$rpart =~ s#filelink\[\s*(\w+)\s*\|([^\]\[]+)\]# $self->remove_filelink($1, $2) #ige
+ and return 1;
+ $$rpart =~ s#filelink\[\s*(\w+)\s*\]# $self->remove_filelink($1) #ige
+ and return 1;
+
$$rpart =~ s#formlink\[(\w+)\|([^\]\[]*)\]#$2#ig
and return 1;
$$rpart =~ s#formlink\[(\w+)\]# $self->remove_formlink($1) #ige
BSE::DB->single->run(siteuserGroupDeleteMember => $self->{id}, $id);
}
+sub contains_user {
+ my ($self, $user) = @_;
+
+ my $user_id = ref $user ? $user->{id} : $user;
+
+ my @membership = BSE::DB->single->query(siteuserMemberOfGroup =>
+ $user_id, $self->{id});
+
+ return scalar @membership;
+}
+
1;
my $sql = $cfg->entry(SECT_QUERY_GROUP_PREFIX.$name, 'sql')
or return;
- return { id => $id, name => $name, sql=>$sql };
+ return bless { id => $id, name => "*$name", sql=>$sql },
+ "BSE::TB::SiteUserQueryGroup";
+}
+
+sub getByName {
+ my ($class, $cfg, $name) = @_;
+
+ if ($name =~ /^\*/) {
+ $name = substr($name, 1);
+
+ my %q_groups = map lc, reverse $cfg->entries(SECT_QUERY_GROUPS);
+ if ($q_groups{lc $name}) {
+ return $class->getQueryGroup($cfg, -$q_groups{lc $name})
+ or return;
+ }
+ else {
+ return;
+ }
+ }
+ else {
+ return $class->getBy(name => $name);
+ }
+}
+
+package BSE::TB::SiteUserQueryGroup;
+
+sub contains_user {
+ my ($self, $user) = @_;
+
+ my $id = ref $user ? $user->{id} : $user;
+
+ my $rows = BSE::DB->single->dbh->selectall_arrayref($self->{sql}, { MaxRows=>1 }, $id);
+ $rows && @$rows
+ and return 1;
+
+ return 0;
}
1;
$self->dyn_iterator('dynchildren_of', 'dynofchild'),
url => [ tag_url => $self ],
ifAncestor => 0,
+ ifUserMemberOf => [ tag_ifUserMemberOf => $self ],
);
}
$req->siteuser_has_access($article);
}
+sub tag_ifUserMemberOf {
+ my ($self, $args, $acts, $func, $templater) = @_;
+
+ my $req = $self->{req};
+
+ my $user = $req->siteuser
+ or return 0; # no user, no group
+
+ my ($name) = DevHelp::Tags->get_parms($args, $acts, $templater);
+
+ $name
+ or return 0; # no group name
+
+ require BSE::TB::SiteUserGroups;
+ my $group = BSE::TB::SiteUserGroups->getByName($req->cfg, $name);
+ unless ($group) {
+ print STDERR "Unknown group name '$name' in ifUserMemberOf\n";
+ return 0;
+ }
+
+ return $group->contains_user($user);
+}
+
sub tag_url {
my ($self, $name, $acts, $func, $templater) = @_;
$value = decode_entities($value);
require Generate;
my $gen = Generate->new(cfg=>$cfg);
- return $gen->format_body($acts, 'Articles', $value, 'tr',
- 1, 0, $templater);
+ return $gen->format_body(acts => $acts,
+ articles => 'Articles',
+ text => $value,
+ templater => $templater);
},
nobodytext => [\&tag_nobodytext, $cfg ],
ifEq =>
sub new {
my ($class, %opts) = @_;
+ unless ($opts{cfg}) {
+ require Carp;
+ Carp->import('confess');
+ confess("cfg missing on generator->new call");
+ }
$opts{maxdepth} = $EMBED_MAX_DEPTH unless exists $opts{maxdepth};
$opts{depth} = 0 unless $opts{depth};
return bless \%opts, $class;
# the formatter now adds <p></p> around the text, but we don't
# want that here
- my $result = $self->format_body({}, $articles, $text, 'tr', 1, 0);
+ my $result = $self->format_body(articles => $articles,
+ text => $text);
$result =~ s!<p>|</p>!!g;
return $result;
return $pos;
}
-# sub _make_hr {
-# my ($width, $height) = @_;
-# my $tag = "<hr";
-# $tag .= qq!width="$width"! if length $width;
-# $tag .= qq!height="$height"! if length $height;
-# $tag .= " />";
-# return $tag;
-# }
-
-# # produces a table, possibly with options for the <table> and <tr> tags
-# sub _make_table {
-# my ($options, $text) = @_;
-# my $tag = "<table";
-# my $cellend = '';
-# my $cellstart = '';
-# if ($options =~ /=/) {
-# $tag .= " " . $options;
-# }
-# elsif ($options =~ /\S/) {
-# $options =~ s/\s+$//;
-# my ($width, $bg, $pad, $fontsz, $fontface) = split /\|/, $options;
-# for ($width, $bg, $pad, $fontsz, $fontface) {
-# $_ = '' unless defined;
-# }
-# $tag .= qq! width="$width"! if length $width;
-# $tag .= qq! bgcolor="$bg"! if length $bg;
-# $tag .= qq! cellpadding="$pad"! if length $pad;
-# if (length $fontsz || length $fontface) {
-# $cellstart = qq!<font!;
-# $cellstart .= qq! size="$fontsz"! if length $fontsz;
-# $cellstart .= qq! face="$fontface"! if length $fontface;
-# $cellstart .= qq!>!;
-# $cellend = "</font>";
-# }
-# }
-# $tag .= ">";
-# my @rows = split '\n', $text;
-# my $maxwidth = 0;
-# for my $row (@rows) {
-# my ($opts, @cols) = split /\|/, $row;
-# $tag .= "<tr";
-# if ($opts =~ /=/) {
-# $tag .= " ".$opts;
-# }
-# $tag .= "><td>$cellstart".join("$cellend</td><td>$cellstart", @cols)
-# ."$cellend</td></tr>";
-# }
-# $tag .= "</table>";
-# return $tag;
-# }
-
-# # make a UL
-# sub _format_bullets {
-# my ($text) = @_;
-
-# $text =~ s/^\s+|\s+$//g;
-# my @points = split /(?:\r?\n)?\*\*\s*/, $text;
-# shift @points if @points and $points[0] eq '';
-# return '' unless @points;
-# for my $point (@points) {
-# $point =~ s!\n$!<br /><br />!;
-# }
-# return "<ul><li>".join("<li>", @points)."</ul>";
-# }
-
-# # make a OL
-# sub _format_ol {
-# my ($text) = @_;
-# $text =~ s/^\s+|\s+$//g;
-# my @points = split /(?:\r?\n)?##\s*/, $text;
-# shift @points if @points and $points[0] eq '';
-# return '' unless @points;
-# for my $point (@points) {
-# #print STDERR "point: ",unpack("H*", $point),"\n";
-# $point =~ s!\n$!<br /><br />!;
-# }
-# return "<ol><li>".join("<li>", @points)."</ol>";
-# }
-
# raw html - this has some limitations
# the input text has already been escaped, so we need to unescape it
# too bad if you want [] in your html (but you can use entities)
# replace markup, insert img tags
sub format_body {
- my ($self, $acts, $articles, $body, $imagePos, $abs_urls,
- $auto_images, $templater, @images) = @_;
+ my $self = shift;
+ my (%opts) =
+ (
+ abs_urls => 0,
+ imagepos => 'tr',
+ auto_images => 1,
+ images => [],
+ files => [],
+ acts => {},
+ @_
+ );
+
+ my $acts = $opts{acts};
+ my $articles = $opts{articles};
+ my $body = $opts{text};
+ my $imagePos = $opts{imagepos};
+ my $abs_urls = $opts{abs_urls};
+ my $auto_images = $opts{auto_images};
+ my $templater = $opts{templater};
+ my $images = $opts{images};
+ my $files = $opts{files};
return substr($body, 6) if $body =~ /^<html>/i;
require BSE::Formatter;
- my $formatter = BSE::Formatter->new($self, $acts, $articles,
- $abs_urls, \$auto_images,
- \@images, $templater);
+ my $formatter = BSE::Formatter->new(gen => $self,
+ acts => $acts,
+ articles => $articles,
+ abs_urls => $abs_urls,
+ auto_images => \$auto_images,
+ images => $images,
+ files => $files,
+ templater => $templater);
$body = $formatter->format($body);
# we don't format named images
- @images = grep $_->{name} eq '', @images;
+ my @images = grep $_->{name} eq '', @$images;
if ($auto_images && @images) {
# the first image simply goes where we're told to put it
# the imagePos is [tb][rl] (top|bottom)(right|left)
require BSE::Formatter;
- my $formatter = BSE::Formatter->new($self, $acts, $articles,
- 1, \0, []);
+ my $formatter = BSE::Formatter->new(gen => $self,
+ acts => $acts,
+ article => $articles);
$$body = $formatter->remove_format($$body);
}
# transform the article or response body (entities, images)
body=>sub {
my ($args, $acts, $funcname, $templater) = @_;
- return $self->format_body($acts, $articles, $article->{body},
- $article->{imagePos}, $abs_urls,
- !$had_image_tags, $templater, @images);
+ return $self->format_body(acts => $acts,
+ article => $articles,
+ text => $article->{body},
+ imagepos => $article->{imagePos},
+ abs_urls => $abs_urls,
+ auto_images => !$had_image_tags,
+ templater => $templater,
+ images => \@images,
+ files => \@files);
},
# used to display a navigation path of parent sections
fix the default template name for the embedded report tags.
+=item *
+
+added dynamic tag ifUserMemberOf which checks if the current user is a
+member of the named group. Supply a leading * to name query groups.
+
+=item *
+
+added a name or identifier field to the article_files table, and
+administration tools to manage it.
+
+=item *
+
+re-worked the formatting API for easier extension
+
+=item *
+
+added filelink[] body text tag
+
+=item *
+
+removed some old commented code from Generate.pm
+
=back
=head2 0.15_36
(blank for guess) </td>
<td nowrap="nowrap" bgcolor="#FFFFFF"><:help file content_type:> <:error_img contentType:></td>
</tr>
+ <tr>
+ <th bgcolor="#FFFFFF" align="left">Identifier:</th>
+ <td bgcolor="#FFFFFF">
+ <input type="text" name="name" value="<:old name:>" /> </td>
+ <td nowrap="nowrap" bgcolor="#FFFFFF"><:help file name:> <:error_img name:></td>
+ </tr>
<tr>
<th bgcolor="#FFFFFF" align="left">Treat as download:</th>
<td bgcolor="#FFFFFF">
</tr>
<: iterator begin files :>
<tr bgcolor="#FFFFFF">
- <td nowrap="nowrap"> <:file displayName:></td>
+ <td nowrap="nowrap" rowspan="2"> <:file displayName:></td>
<td valign="top">
<:ifUserCan edit_files_save:article:><input name="description_<:file id:>" type="text" value="<: file description :>" size="35" />
<:or:><: file description :><:eif:>
<:ifUserCan edit_files_save:article:><input name="contentType_<:file id:>" type="text" value="<: file contentType :>" size="20" />
<:or:><: file contentType :><:eif:>
</td>
- <td valign="top" rowspan="2">
+ <td valign="top" rowspan="3">
<:ifUserCan edit_files_save:article:><textarea name="notes_<:file id:>" cols="40" rows="5"><:file notes:></textarea><:or:><:replace [file notes] "
" "<br />" :><:eif:>
</td>
</tr>
+ <tr>
+ <td valign="top" colspan="2" nowrap="nowrap" bgcolor="#FFFFFF">
+ Identfier: <:ifUserCan edit_files_save:article:><input name="name_<:file id:>" type="text" value="<: file name :>" size="20" />
+ <:or:><: file name :><:eif:>
+ </td>
+ </tr>
<tr bgcolor="#FFFFFF">
<td colspan="3">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
</tr>
<: iterator separator files :>
<tr bgcolor="#FFFFFF">
- <td colspan="3"> </td>
+ <td colspan="4"> </td>
</tr>
<: iterator end files :>
<:ifUserCan edit_files_save:article:>