1 package BSE::TB::Image;
3 # represents an image from the database
8 @ISA = qw/Squirrel::Row BSE::ThumbCommon BSE::TB::TagOwner/;
11 our $VERSION = "1.011";
15 BSE::TB::Image - images attached to an article or a global image.
19 my @images = $article->images;
23 X<images>This class represents an image attached to an article, or a
32 Unique id for this image.
36 article this image belongs to. C<-1> for global images.
40 image filename as stored in the images directory. See C</image_url>
41 to get a URL to the image.
45 alternate text for the image
51 X<image, width>X<image, height>width and height of the
56 url to link to when the image is inlined.
60 sort key for ordering images belonging to an article (or within the
61 global image collection.)
65 unique name for the image within the images belonging to an article
66 (or withing the global image collection.) Can be an empty string.
70 the external storage used for the image, or C<local> for locally
75 for externally stored images, the URL to the image. Use
80 the type of image, either C<img> for normal images, C<svg> for SVG
81 images or C<flash> for flash files.
86 return qw/id articleId image alt width height url displayOrder name
94 Call the format() image handler object for this image.
96 Accepts the following parameters:
102 C<align> - sets the align attribute.
106 C<extras> - extra C<img> tag attributes.
115 my ($self, %opts) = @_;
117 my $cfg = delete $opts{cfg}
118 or confess "Missing cfg parameter";
120 my $handler = $self->_handler_object($cfg);
122 return $handler->format
131 Inline the image, accepts the following parameters:
137 C<align> - set class to C<< bse_image_I<align> >>.
146 my ($self, %opts) = @_;
148 my $cfg = delete $opts{cfg} || BSE::Cfg->single;
150 my $handler = $self->_handler_object($cfg);
152 return $handler->inline
161 Call the popimage() image handler object for this image, displaying
162 the image as a thumbnail that displays a larger version when clicked.
170 C<class> - controls the section of the config file that popup
171 parameters are taken from.
175 C<static> - true to use static URLs for the thumbnails.
184 my ($im, %opts) = @_;
186 my $cfg = delete $opts{cfg}
187 or confess "Missing cfg parameter";
189 my $handler = $im->_handler_object($cfg);
191 return $handler->popimage
200 Return the image's source URL. This will be the storage URL if the
201 image is C<storage> is not C<local>.
208 return $im->src || BSE::TB::Images->base_uri . $im->image;
213 Returns the image data as a data structure suitable for conversion to
221 my $data = $self->data_only;
222 $data->{url} = $self->image_url;
223 $data->{tags} = [ $self->tags ];
228 =item dynamic_thumb_url(...)
230 Return a dynamic URL to a thumbnail of the image.
232 Requires one named parameter:
238 C<geo> - the thumbnail geometry to use.
244 sub dynamic_thumb_url {
245 my ($self, %opts) = @_;
247 my $geo = delete $opts{geo}
248 or Carp::confess("missing geo option");
250 return $self->thumb_base_url
251 . "?g=$geo&page=$self->{articleId}&image=$self->{id}";
261 return BSE::TB::Images->image_dir() . "/" . $self->image;
264 # compatibility with BSE::TB::File
273 The article this image belongs to.
280 if ($self->articleId == -1) {
281 require BSE::TB::Site;
282 return BSE::TB::Site->new;
285 require BSE::TB::Articles;
286 return BSE::TB::Articles->getByPkey($self->articleId);
300 unlink $self->full_filename;
301 return $self->SUPER::remove();
306 Make updates to the image.
311 my ($image, %opts) = @_;
313 my $errors = delete $opts{errors}
314 or confess "Missing errors parameter";
316 my $actor = $opts{_actor}
317 or confess "Missing _actor parameter";
319 my $warnings = $opts{_warnings}
320 or confess "Missing _warnings parameter";
322 require BSE::CfgInfo;
323 my $cfg = BSE::Cfg->single;
324 my $image_dir = BSE::CfgInfo::cfg_image_dir($cfg);
328 my $old_storage = $image->storage;
331 $filename = $opts{display_name}
332 or confess "Missing display_name";
334 elsif ($opts{file}) {
335 unless (open $fh, "<", $opts{file}) {
336 $errors->{filename} = "Cannot open $opts{file}: $!";
340 $filename = $opts{file};
346 require DevHelp::FileUpload;
347 my ($image_name) = DevHelp::FileUpload->
348 make_fh_copy($fh, $image_dir, $filename, \$msg)
351 my $full_filename = "$image_dir/$image_name";
352 require BSE::ImageSize;
353 my ($width, $height, $type) = BSE::ImageSize::imgsize($full_filename);
355 $delete_file = $image->image;
356 $image->set_image($image_name);
357 $image->set_width($width);
358 $image->set_height($height);
359 $image->set_storage("local");
360 $image->set_src(BSE::TB::Images->base_uri . $image_name);
361 $image->set_ftype(BSE::TB::Images->get_ftype($type));
369 chomp($errors->{$fh_field} = $@);
373 my $name = $opts{name};
375 unless ($name =~ /^[a-z_]\w*$/i) {
376 $errors->{name} = "msg:bse/admin/edit/image/save/nameformat:$name";
378 if (!$errors->{name} && length $name && $name ne $image->name) {
379 # check for a duplicate
380 my @other_images = grep $_->id != $image->id, $image->article->images;
381 if (grep $name eq $_->name, @other_images) {
382 $errors->{name} = "msg:bse/admin/edit/image/save/namedup:$name";
387 if (defined $opts{alt}) {
388 $image->set_alt($opts{alt});
391 if (defined $opts{url}) {
392 $image->set_url($opts{url});
398 my $new_storage = $opts{storage};
399 defined $new_storage or $new_storage = $image->storage;
402 my $mgr = BSE::TB::Images->storage_manager;
405 if ($old_storage ne "local") {
406 $mgr->unstore($delete_file);
408 unlink "$image_dir/$delete_file";
410 $old_storage = "local";
413 # try to set the storage, this failing doesn't fail the save
416 $mgr->select_store($image->image, $new_storage, $image);
417 if ($image->storage ne $new_storage) {
418 # handles both new images (which sets storage to local) and changing
419 # the storage for old images
420 $old_storage = $image->storage;
421 my $src = $mgr->store($image->image, $new_storage, $image);
422 $image->set_src($src);
423 $image->set_storage($new_storage);
430 require BSE::TB::AuditLog;
431 BSE::TB::AuditLog->log
433 component => "admin:edit:saveimage",
437 msg => "Error saving image to storage $new_storage: $msg",
439 push @$warnings, "msg:bse/admin/edit/image/save/savetostore:$msg";
442 if ($image->storage ne $old_storage && $old_storage ne "local") {
444 $mgr->unstore($image->image, $old_storage);
449 require BSE::TB::AuditLog;
450 BSE::TB::AuditLog->log
452 component => "admin:edit:saveimage",
456 msg => "Error saving image to storage $new_storage: $msg",
458 push @$warnings, "msg:bse/admin/edit/image/save/delfromstore:$msg";
477 =head1 INHERITED BEHAVIOUR
479 Inherits from L<BSE::TB::TagOwner> and L<BSE::ThumbCommon>
483 Tony Cook <tony@develop-help.com>