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.010";
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}
149 or confess "Missing cfg parameter";
151 my $handler = $self->_handler_object($cfg);
153 return $handler->inline
162 Call the popimage() image handler object for this image, displaying
163 the image as a thumbnail that displays a larger version when clicked.
171 C<class> - controls the section of the config file that popup
172 parameters are taken from.
176 C<static> - true to use static URLs for the thumbnails.
185 my ($im, %opts) = @_;
187 my $cfg = delete $opts{cfg}
188 or confess "Missing cfg parameter";
190 my $handler = $im->_handler_object($cfg);
192 return $handler->popimage
201 Return the image's source URL. This will be the storage URL if the
202 image is C<storage> is not C<local>.
209 return $im->src || BSE::TB::Images->base_uri . $im->image;
214 Returns the image data as a data structure suitable for conversion to
222 my $data = $self->data_only;
223 $data->{url} = $self->image_url;
224 $data->{tags} = [ $self->tags ];
229 =item dynamic_thumb_url(...)
231 Return a dynamic URL to a thumbnail of the image.
233 Requires one named parameter:
239 C<geo> - the thumbnail geometry to use.
245 sub dynamic_thumb_url {
246 my ($self, %opts) = @_;
248 my $geo = delete $opts{geo}
249 or Carp::confess("missing geo option");
251 return $self->thumb_base_url
252 . "?g=$geo&page=$self->{articleId}&image=$self->{id}";
262 return BSE::TB::Images->image_dir() . "/" . $self->image;
265 # compatibility with BSE::TB::File
274 The article this image belongs to.
281 if ($self->articleId == -1) {
282 require BSE::TB::Site;
283 return BSE::TB::Site->new;
286 require BSE::TB::Articles;
287 return BSE::TB::Articles->getByPkey($self->articleId);
301 unlink $self->full_filename;
302 return $self->SUPER::remove();
307 Make updates to the image.
312 my ($image, %opts) = @_;
314 my $errors = delete $opts{errors}
315 or confess "Missing errors parameter";
317 my $actor = $opts{_actor}
318 or confess "Missing _actor parameter";
320 my $warnings = $opts{_warnings}
321 or confess "Missing _warnings parameter";
323 require BSE::CfgInfo;
324 my $cfg = BSE::Cfg->single;
325 my $image_dir = BSE::CfgInfo::cfg_image_dir($cfg);
329 my $old_storage = $image->storage;
332 $filename = $opts{display_name}
333 or confess "Missing display_name";
335 elsif ($opts{file}) {
336 unless (open $fh, "<", $opts{file}) {
337 $errors->{filename} = "Cannot open $opts{file}: $!";
341 $filename = $opts{file};
347 require DevHelp::FileUpload;
348 my ($image_name) = DevHelp::FileUpload->
349 make_fh_copy($fh, $image_dir, $filename, \$msg)
352 my $full_filename = "$image_dir/$image_name";
353 require BSE::ImageSize;
354 my ($width, $height, $type) = BSE::ImageSize::imgsize($full_filename);
356 $delete_file = $image->image;
357 $image->set_image($image_name);
358 $image->set_width($width);
359 $image->set_height($height);
360 $image->set_storage("local");
361 $image->set_src(BSE::TB::Images->base_uri . $image_name);
362 $image->set_ftype(BSE::TB::Images->get_ftype($type));
370 chomp($errors->{$fh_field} = $@);
374 my $name = $opts{name};
376 unless ($name =~ /^[a-z_]\w*$/i) {
377 $errors->{name} = "msg:bse/admin/edit/image/save/nameformat:$name";
379 if (!$errors->{name} && length $name && $name ne $image->name) {
380 # check for a duplicate
381 my @other_images = grep $_->id != $image->id, $image->article->images;
382 if (grep $name eq $_->name, @other_images) {
383 $errors->{name} = "msg:bse/admin/edit/image/save/namedup:$name";
388 if (defined $opts{alt}) {
389 $image->set_alt($opts{alt});
392 if (defined $opts{url}) {
393 $image->set_url($opts{url});
399 my $new_storage = $opts{storage};
400 defined $new_storage or $new_storage = $image->storage;
403 my $mgr = BSE::TB::Images->storage_manager;
406 if ($old_storage ne "local") {
407 $mgr->unstore($delete_file);
409 unlink "$image_dir/$delete_file";
411 $old_storage = "local";
414 # try to set the storage, this failing doesn't fail the save
417 $mgr->select_store($image->image, $new_storage, $image);
418 if ($image->storage ne $new_storage) {
419 # handles both new images (which sets storage to local) and changing
420 # the storage for old images
421 $old_storage = $image->storage;
422 my $src = $mgr->store($image->image, $new_storage, $image);
423 $image->set_src($src);
424 $image->set_storage($new_storage);
431 require BSE::TB::AuditLog;
432 BSE::TB::AuditLog->log
434 component => "admin:edit:saveimage",
438 msg => "Error saving image to storage $new_storage: $msg",
440 push @$warnings, "msg:bse/admin/edit/image/save/savetostore:$msg";
443 if ($image->storage ne $old_storage && $old_storage ne "local") {
445 $mgr->unstore($image->image, $old_storage);
450 require BSE::TB::AuditLog;
451 BSE::TB::AuditLog->log
453 component => "admin:edit:saveimage",
457 msg => "Error saving image to storage $new_storage: $msg",
459 push @$warnings, "msg:bse/admin/edit/image/save/delfromstore:$msg";
478 =head1 INHERITED BEHAVIOUR
480 Inherits from L<BSE::TB::TagOwner> and L<BSE::ThumbCommon>
484 Tony Cook <tony@develop-help.com>