root/branches/release-40/lib/MT/Asset/Image.pm @ 2561

Revision 2561, 22.9 kB (checked in by bchoate, 18 months ago)

Require popup_asset_id parameter, to avoid loading first available asset if it isn't provided.

Line 
1# Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd.
2# This program is distributed under the terms of the
3# GNU General Public License, version 2.
4#
5# $Id$
6
7package MT::Asset::Image;
8
9use strict;
10use base qw( MT::Asset );
11
12__PACKAGE__->install_properties( {
13    class_type => 'image',
14    column_defs => {
15        'image_width' => 'integer meta',
16        'image_height' => 'integer meta',
17    },
18} );
19
20# List of supported file extensions (to aid the stock 'can_handle' method.)
21sub extensions { [ qr/gif/i, qr/jpe?g/i, qr/png/i, ] }
22
23sub class_label {
24    MT->translate('Image');
25}
26
27sub class_label_plural {
28    MT->translate('Images');
29}
30
31sub metadata {
32    my $obj  = shift;
33    my $meta = $obj->SUPER::metadata(@_);
34
35    my $width  = $obj->image_width;
36    my $height = $obj->image_height;
37    $meta->{image_width}  = $width  if defined $width;
38    $meta->{image_height} = $height if defined $height;
39    $meta->{image_dimensions} = $meta->{ MT->translate("Actual Dimensions") } =
40      MT->translate( "[_1] x [_2] pixels", $width, $height )
41      if defined $width && defined $height;
42
43    $meta;
44}
45
46sub image_height {
47    my $asset = shift;
48    my $height = $asset->meta('image_height', @_);
49    return $height if $height || @_;
50
51    eval { require Image::Size; };
52    return undef if $@;
53    my ( $w, $h, $id ) = Image::Size::imgsize($asset->file_path);
54    $asset->meta('image_height', $h);
55    if ($asset->id) {
56        $asset->save;
57    }
58    return $h;
59}
60
61sub image_width {
62    my $asset = shift;
63    my $width = $asset->meta('image_width', @_);
64    return $width if $width || @_;
65
66    eval { require Image::Size; };
67    return undef if $@;
68    my ( $w, $h, $id ) = Image::Size::imgsize($asset->file_path);
69    $asset->meta('image_width', $w);
70    if ($asset->id) {
71        $asset->save;
72    }
73    return $w;
74}
75
76sub has_thumbnail {
77    1;
78}
79
80sub thumbnail_path {
81    my $asset   = shift;
82    my (%param) = @_;
83
84    $asset->_make_cache_path($param{Path});
85}
86
87sub thumbnail_file {
88    my $asset     = shift;
89    my (%param)   = @_;
90    my $file_path = $asset->file_path;
91    my @imginfo   = stat($file_path);
92    return undef unless @imginfo;
93
94    my $blog = $param{Blog} || $asset->blog;
95    my $fmgr;
96
97    require MT::Util;
98    my $asset_cache_path = $asset->_make_cache_path($param{Path});
99    my ( $i_h, $i_w ) = ( $asset->image_height, $asset->image_width );
100    return undef unless $i_h && $i_w;
101
102    # Pretend the image is already square, for calculation purposes.
103    if ($param{Square}) {
104        require MT::Image;
105        my %square = MT::Image->inscribe_square(
106            Width => $i_w, Height => $i_h );
107        ($i_h, $i_w) = @square{qw( Size Size )};
108        if ( $param{Width} && !$param{Height} ) {
109            $param{Height} = $param{Width};
110        }
111        elsif ( !$param{Width} && $param{Height} ) {
112            $param{Width} = $param{Height};
113        }
114    }
115
116    if ( my $scale = $param{Scale} ) {
117        $param{Width}  = int( ( $i_w * $scale ) / 100 );
118        $param{Height} = int( ( $i_h * $scale ) / 100 );
119    }
120    if ( !exists $param{Width} && !exists $param{Height} ) {
121        $param{Width}  = $i_w;
122        $param{Height} = $i_h;
123    }
124
125    # find the longest dimension of the image:
126    my ( $n_h, $n_w ) =
127      _get_dimension( $i_h, $i_w, $param{Height}, $param{Width} );
128
129    my $file = $asset->thumbnail_filename(%param) or return;
130    my $thumbnail = File::Spec->catfile( $asset_cache_path, $file );
131    my @thumbinfo = stat($thumbnail);
132
133    # thumbnail file exists and is dated on or later than source image
134    if ( @thumbinfo && ( $thumbinfo[9] >= $imginfo[9] ) ) {
135        return ( $thumbnail, $n_w, $n_h );
136    }
137
138    # stale or non-existent thumbnail. let's create one!
139    require MT::FileMgr;
140    $fmgr ||= $blog ? $blog->file_mgr : MT::FileMgr->new('Local');
141    return undef unless $fmgr;
142    return undef unless $fmgr->can_write($asset_cache_path);
143
144    my $data;
145    if ( ( $n_w == $i_w ) && ( $n_h == $i_h ) && !$param{Square}
146      && !$param{Type} ) {
147        $data = $fmgr->get_data( $file_path, 'upload' );
148    }
149    else {
150
151        # create a thumbnail for this file
152        require MT::Image;
153        my $img = new MT::Image( Filename => $file_path )
154          or return $asset->error( MT::Image->errstr );
155
156        # Really make the image square, so our scale calculation works out.
157        if ($param{Square}) {
158            ($data) = $img->make_square()
159              or return $asset->error(
160                MT->translate( "Error cropping image: [_1]", $img->errstr ) );
161        }
162
163        ($data) = $img->scale( Height => $n_h, Width => $n_w )
164          or return $asset->error(
165            MT->translate( "Error scaling image: [_1]", $img->errstr ) );
166
167        if (my $type = $param{Type}) {
168            ($data) = $img->convert( Type => $type )
169              or return $asset->error(
170                MT->translate( "Error converting image: [_1]", $img->errstr ) );
171        }
172    }
173    $fmgr->put_data( $data, $thumbnail, 'upload' )
174      or return $asset->error(
175        MT->translate( "Error creating thumbnail file: [_1]", $fmgr->errstr ) );
176    return ( $thumbnail, $n_w, $n_h );
177}
178
179sub _get_dimension {
180    my ( $i_h, $i_w, $h, $w ) = @_;
181
182    my ( $n_h, $n_w ) = ( $i_h, $i_w );
183    my $scale = '';
184    if ( $h && !$w ) {
185        $scale = 'h';
186    }
187    elsif ( $w && !$h ) {
188        $scale = 'w';
189    }
190    else {
191        if ( $i_h > $i_w ) {
192
193            # scale, if necessary, by height
194            if ( $i_h > $h ) {
195                $scale = 'h';
196            }
197            elsif ( $i_w > $w ) {
198                $scale = 'w';
199            }
200        }
201        else {
202
203            # scale, if necessary, by width
204            if ( $i_w > $w ) {
205                $scale = 'w';
206            }
207            elsif ( $i_h > $h ) {
208                $scale = 'h';
209            }
210        }
211    }
212    if ( $scale eq 'h' ) {
213
214        # scale by height
215        $n_h = $h;
216        $n_w = int( $i_w * $h / $i_h );
217    }
218    elsif ( $scale eq 'w' ) {
219
220        # scale by width
221        $n_w = $w;
222        $n_h = int( $i_h * $w / $i_w );
223    }
224    return ( $n_h, $n_w );
225}
226
227sub thumbnail_filename {
228    my $asset   = shift;
229    my (%param) = @_;
230    my $file    = $asset->file_name or return;
231
232    require MT::Util;
233    my $format = $param{Format} || MT->translate('%f-thumb-%wx%h%x');
234    my $width  = $param{Width}  || 'auto';
235    my $height = $param{Height} || 'auto';
236    $file =~ s/\.\w+$//;
237    my $base = File::Basename::basename($file);
238    my $id   = $asset->id;
239    my $ext  = lc($param{Type}) || $asset->file_ext || '';
240    $ext = '.' . $ext;
241    $format =~ s/%w/$width/g;
242    $format =~ s/%h/$height/g;
243    $format =~ s/%f/$base/g;
244    $format =~ s/%i/$id/g;
245    $format =~ s/%x/$ext/g;
246    return $format;
247}
248
249sub as_html {
250    my $asset   = shift;
251    my ($param) = @_;
252    my $text    = '';
253
254    $param->{enclose} = 1 unless exists $param->{enclose};
255
256    if ( $param->{include} ) {
257
258        my $fname = $asset->file_name;
259        require MT::Util;
260
261        my $thumb = undef;
262        if ( $param->{thumb} ) {
263            $thumb = MT::Asset->load( $param->{thumb_asset_id} )
264              || return $asset->error(
265                MT->translate(
266                    "Can't load image #[_1]",
267                    $param->{thumb_asset_id}
268                )
269              );
270        }
271
272        my $dimensions = sprintf(
273            'width="%s" height="%s"',
274            (
275                $thumb
276                ? ( $thumb->image_width, $thumb->image_height )
277                : ( $asset->image_width, $asset->image_height )
278            )
279        );
280        my $wrap_style = '';
281        if ( $param->{wrap_text} && $param->{align} ) {
282            $wrap_style = 'class="mt-image-' . $param->{align} . '" ';
283            if ( $param->{align} eq 'none' ) {
284                $wrap_style .= q{style=""};
285            }
286            elsif ( $param->{align} eq 'left' ) {
287                $wrap_style .= q{style="float: left; margin: 0 20px 20px 0;"};
288            }
289            elsif ( $param->{align} eq 'right' ) {
290                $wrap_style .= q{style="float: right; margin: 0 0 20px 20px;"};
291            }
292            elsif ( $param->{align} eq 'center' ) {
293                $wrap_style .= q{style="text-align: center; display: block; margin: 0 auto 20px;"};
294            }
295        }
296
297        if ( $param->{popup} && $param->{popup_asset_id} ) {
298            my $popup = MT::Asset->load( $param->{popup_asset_id} )
299              || return $asset->error(
300                MT->translate(
301                    "Can't load image #[_1]",
302                    $param->{popup_asset_id}
303                )
304              );
305            my $link =
306              $thumb
307              ? sprintf(
308                '<img src="%s" %s alt="%s" %s />',
309                MT::Util::encode_html( $thumb->url ),   $dimensions,
310                MT::Util::encode_html( $asset->label ), $wrap_style
311              )
312              : MT->translate('View image');
313            $text = sprintf(
314q|<a href="%s" onclick="window.open('%s','popup','width=%d,height=%d,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0'); return false">%s</a>|,
315                MT::Util::encode_html( $popup->url ),
316                MT::Util::encode_html( $popup->url ),
317                $asset->image_width,
318                $asset->image_height,
319                $link,
320            );
321        }
322        else {
323            if ( $param->{thumb} ) {
324                $text = sprintf(
325                    '<a href="%s"><img alt="%s" src="%s" %s %s /></a>',
326                    MT::Util::encode_html( $asset->url ),
327                    MT::Util::encode_html( $asset->label ),
328                    MT::Util::encode_html( $thumb->url ),
329                    $dimensions,
330                    $wrap_style,
331                );
332            }
333            else {
334                $text = sprintf(
335                    '<img alt="%s" src="%s" %s %s />',
336                    MT::Util::encode_html( $asset->label ),
337                    MT::Util::encode_html( $asset->url ),
338                    $dimensions, $wrap_style,
339                );
340            }
341        }
342    }
343    else {
344        $text = sprintf(
345            '<a href="%s">%s</a>',
346            MT::Util::encode_html( $asset->url ),
347            MT->translate('View image'),
348        );
349    }
350
351    return $param->{enclose} ? $asset->enclose($text) : $text;
352}
353
354# Return a HTML snippet of form options for inserting this asset
355# into a web page. Default behavior is no options.
356sub insert_options {
357    my $asset = shift;
358    my ($param) = @_;
359
360    my $app   = MT->instance;
361    my $perms = $app->{perms};
362    my $blog  = $asset->blog or return;
363
364    eval { require MT::Image; MT::Image->new or die; };
365    $param->{do_thumb} = $@ ? 0 : 1;
366
367    $param->{can_save_image_defaults} = $perms->can_save_image_defaults ? 1 : 0;
368
369    #$param->{constrain} = $blog->image_default_constrain ? 1 : 0;
370    $param->{popup}      = $blog->image_default_popup     ? 1 : 0;
371    $param->{wrap_text}  = $blog->image_default_wrap_text ? 1 : 0;
372    $param->{make_thumb} = $blog->image_default_thumb     ? 1 : 0;
373    $param->{ 'align_' . $_ } =
374      ( $blog->image_default_align || 'none' ) eq $_ ? 1 : 0
375      for qw(none left center right);
376    $param->{ 'unit_w' . $_ } =
377      ( $blog->image_default_wunits || 'pixels' ) eq $_ ? 1 : 0
378      for qw(percent pixels);
379    $param->{thumb_width} = $blog->image_default_width
380      || $asset->image_width
381      || 0;
382
383    return $app->build_page( 'dialog/asset_options_image.tmpl', $param );
384}
385
386sub on_upload {
387    my $asset = shift;
388    my ($param) = @_;
389
390    $asset->SUPER::on_upload(@_);
391
392    return unless $param->{new_entry};
393
394    my $app = MT->instance;
395    require MT::Util;
396
397    my $url    = $asset->url;
398    my $width  = $asset->image_width;
399    my $height = $asset->image_height;
400
401    my ( $base_url, $fname ) = $url =~ m|(.*)/([^/]*)|;
402    $url =
403        $base_url . '/'
404      . $fname;    # no need to re-encode filename; url is already encoded
405    my $blog = $asset->blog or return;
406    my $blog_id = $blog->id;
407
408    my ( $thumb, $thumb_width, $thumb_height );
409    $thumb_width = $param->{thumb_width};
410    $thumb       = $param->{thumb};
411    if ($thumb) {
412        if ( $thumb_width && ( $thumb_width !~ m/^\d+$/ ) ) {
413            undef $thumb_width;
414        }
415
416        # width > 1000 not really a thumbnail, so consider invalid
417        if ( $thumb_width > 1000 ) {
418            undef $thumb_width;
419        }
420    }
421    if ( $thumb && !$thumb_width ) {
422        undef $thumb;
423    }
424    if ( $param->{image_defaults} ) {
425        return $app->error(
426            $app->translate(
427                'Permission denied setting image defaults for blog #[_1]',
428                $blog_id
429            )
430        ) unless $app->{perms}->can_save_image_defaults;
431
432        # Save new defaults if requested.
433        $blog->image_default_wrap_text( $param->{wrap_text} ? 1 : 0 );
434        $blog->image_default_align( $param->{align} || MT::Blog::ALIGN() );
435        if ($thumb) {
436            $blog->image_default_thumb(1);
437            $blog->image_default_width($thumb_width);
438            $blog->image_default_wunits( $param->{thumb_width_type}
439                  || MT::Blog::UNITS() );
440        }
441        else {
442            $blog->image_default_thumb(0);
443            $blog->image_default_width(0);
444            $blog->image_default_wunits( MT::Blog::UNITS() );
445        }
446
447        #$blog->image_default_constrain($param->{constrain} ? 1 : 0);
448        $blog->image_default_popup( $param->{popup} ? 1 : 0 );
449        $blog->save or die $blog->errstr;
450    }
451
452    require MT::Util;
453    my $extra_path = undef;
454    my $extra_url = '';
455    if (defined $param->{middle_path} || defined $param->{extra_path}) {
456        my $middle_path = $param->{middle_path} || '';
457        my @split_path = split( '/', $middle_path );
458        $extra_path = '';
459
460        for my $middle (@split_path) {
461            $extra_path = File::Spec->catfile( $extra_path, $middle );
462        }
463        $extra_path = File::Spec->catfile( $extra_path, $param->{extra_path} ) if ($param->{extra_path});
464        $extra_url = MT::Util::caturl($middle_path, ($param->{extra_path} || ''));
465    }
466
467    # Thumbnail creation
468    if ( $thumb = $param->{thumb} ) {
469        require MT::Image;
470        my $image_type = scalar $param->{image_type};
471        my ( $w, $h ) = map $param->{$_}, qw( thumb_width thumb_height );
472        my ($pseudo_thumbnail_url) =
473          $asset->thumbnail_url( Height => $h, Width => $w, Path => $extra_path, Pseudo => 1 );
474        my $thumbnail = $asset->thumbnail_filename( Height => $h, Width => $w );
475        my $pseudo_thumbnail_path = File::Spec->catfile($asset->_make_cache_path($extra_path, 1), $thumbnail);
476        my ( $base, $path, $ext ) =
477          File::Basename::fileparse( $thumbnail, qr/[A-Za-z0-9]+$/ );
478        my $img_pkg         = MT::Asset->handler_for_file($thumbnail);
479        my $original;
480        my $asset_thumb = $img_pkg->load({
481            file_name => "$base$ext",
482            parent   => $asset->id,});
483        if (!$asset_thumb) {
484            $asset_thumb     = new $img_pkg;
485            $original        = $asset_thumb->clone;
486            $asset_thumb->blog_id($blog_id);
487            $asset_thumb->url($pseudo_thumbnail_url);
488            $asset_thumb->file_path($pseudo_thumbnail_path);
489            $asset_thumb->file_name("$base$ext");
490            $asset_thumb->file_ext($ext);
491            $asset_thumb->image_width($w);
492            $asset_thumb->image_height($h);
493            $asset_thumb->created_by( $app->user->id );
494            $asset_thumb->label($app->translate("Thumbnail image for [_1]", $asset->label || $asset->file_name));
495            $asset_thumb->parent( $asset->id );
496            $asset_thumb->save;
497        } else {
498            $original = $asset_thumb->clone;
499        }
500
501        # force these to calculate now, giving a full URL / file path
502        # for callbacks
503        $thumbnail = $asset_thumb->file_path;
504        my $thumbnail_url = $asset_thumb->url;
505        my $thumb_file_size = ( stat($thumbnail) )[7];
506
507        $app->run_callbacks( 'cms_post_save.asset', $app, $asset_thumb,
508            $original );
509
510        $param->{thumb_asset_id} = $asset_thumb->id;
511
512        $app->run_callbacks(
513            'cms_upload_file.' . $asset_thumb->class,
514            File  => $thumbnail,
515            file  => $thumbnail,
516            Url   => $thumbnail_url,
517            url   => $thumbnail_url,
518            Size  => $thumb_file_size,
519            size  => $thumb_file_size,
520            Asset => $asset_thumb,
521            asset => $asset_thumb,
522            Type  => 'thumbnail',
523            type  => 'thumbnail',
524            Blog  => $blog,
525            blog  => $blog
526        );
527
528        $app->run_callbacks(
529            'cms_upload_image',
530            File       => $thumbnail,
531            file       => $thumbnail,
532            Url        => $thumbnail_url,
533            url        => $thumbnail_url,
534            Asset      => $asset_thumb,
535            asset      => $asset_thumb,
536            Width      => $w,
537            width      => $w,
538            Height     => $h,
539            height     => $h,
540            ImageType  => $image_type,
541            image_type => $image_type,
542            Size       => $thumb_file_size,
543            size       => $thumb_file_size,
544            Type       => 'thumbnail',
545            type       => 'thumbnail',
546            Blog       => $blog,
547            blog       => $blog
548        );
549    }
550    if ( $param->{popup} ) {
551        require MT::Template;
552        if (
553            my $tmpl = MT::Template->load(
554                {
555                    blog_id => $blog_id,
556                    type    => 'popup_image'
557                }
558            )
559          )
560        {
561            ( my $rel_path = $param->{fname} ) =~ s!\.[^.]*$!!;
562            if ( $rel_path =~ m!\.\.|\0|\|! ) {
563                return $app->error(
564                    $app->translate( "Invalid basename '[_1]'", $rel_path ) );
565            }
566            my $ext = $blog->file_extension || '';
567            $ext = '.' . $ext if $ext ne '';
568            require MT::Template::Context;
569            my $ctx = MT::Template::Context->new;
570            $ctx->stash( 'blog',         $blog );
571            $ctx->stash( 'blog_id',      $blog->id );
572            $ctx->stash( 'asset',        $asset );
573            $ctx->stash( 'image_url',    $url );
574            $ctx->stash( 'image_width',  $width );
575            $ctx->stash( 'image_height', $height );
576            my $popup = $tmpl->build($ctx) or die $tmpl->errstr;
577            my $fmgr = $blog->file_mgr;
578            my $root_path =
579              $param->{site_path} ? $blog->site_path : $blog->archive_path;
580            my $pseudo_path = $param->{site_path} ? '%r' : '%a';
581            $root_path =
582              File::Spec->catfile( $root_path, ($extra_path || '') );
583            $pseudo_path = File::Spec->catfile( $pseudo_path, ($extra_path || '') );
584            my $abs_file_path =
585              File::Spec->catfile( $root_path, $rel_path . $ext );
586
587            ## If the popup filename already exists, we don't want to overwrite
588            ## it, because it could contain valuable data; so we'll just make
589            ## sure to generate the name uniquely.
590            my ( $i, $rel_path_ext ) = ( 0, $rel_path . $ext );
591            while ( $fmgr->exists($abs_file_path) ) {
592                $rel_path_ext = $rel_path . ++$i . $ext;
593                $abs_file_path =
594                  File::Spec->catfile( $root_path, $rel_path_ext );
595            }
596            $pseudo_path = File::Spec->catfile( $pseudo_path, $rel_path_ext );
597            my ( $vol, $dirs, $basename ) =
598              File::Spec->splitpath($rel_path_ext);
599            my $rel_url_ext =
600              File::Spec->catpath( $vol, $dirs,
601                MT::Util::encode_url($basename) );
602
603            ## Untaint. We have checked for security holes above, so we
604            ## should be safe.
605            ($abs_file_path) = $abs_file_path =~ /(.+)/s;
606            $fmgr->put_data( $popup, $abs_file_path, 'upload' )
607              or return $app->error(
608                $app->translate(
609                    "Error writing to '[_1]': [_2]", $abs_file_path,
610                    $fmgr->errstr
611                )
612              );
613            $url = $param->{site_path} ? '%r' : '%a';
614            $rel_url_ext =~ s!^/!!;
615            $url = MT::Util::caturl($url, $extra_url, $rel_url_ext);
616
617            my $html_pkg   = MT::Asset->handler_for_file($abs_file_path);
618            my $original;
619            my $asset_html = $html_pkg->load({
620                file_name => "$basename",
621                parent => $asset->id});
622            if (!$asset_html) {
623                $asset_html = new $html_pkg;
624                $original   = $asset_html->clone;
625                $asset_html->blog_id($blog_id);
626                $asset_html->url($url);
627                $asset_html->label($app->translate("Popup Page for [_1]", $asset->label || $asset->file_name));
628                $asset_html->file_path($pseudo_path);
629                $asset_html->file_name($basename);
630                $asset_html->file_ext( $blog->file_extension );
631                $asset_html->created_by( $app->user->id );
632                $asset_html->parent( $asset->id );
633                $asset_html->save;
634            } else {
635                $original   = $asset_html->clone;
636            }               
637
638            # Select back the real URL for callbacks
639            $url = $asset_html->url;
640
641            $param->{popup_asset_id} = $asset_html->id;
642
643            $app->run_callbacks( 'cms_post_save.asset', $app, $asset_html,
644                $original );
645
646            $app->run_callbacks(
647                'cms_upload_file.' . $asset_html->class,
648                File  => $abs_file_path,
649                file  => $abs_file_path,
650                Url   => $url,
651                url   => $url,
652                Asset => $asset_html,
653                asset => $asset_html,
654                Size  => length($popup),
655                size  => length($popup),
656                Type  => 'popup',
657                type  => 'popup',
658                Blog  => $blog,
659                blog  => $blog
660            );
661        }
662    }
663    1;
664}
665
666sub edit_template_param {
667    my $asset = shift;
668    my ($cb, $app, $param, $tmpl) = @_;
669
670    $param->{image_height} = $asset->image_height;
671    $param->{image_width}  = $asset->image_width;
672}
673
6741;
675
676__END__
677
678=head1 NAME
679
680MT::Asset::Image
681
682=head1 SYNOPSIS
683
684    use MT::Asset::Image;
685
686    # Example
687
688=head1 DESCRIPTION
689
690=head1 METHODS
691
692=head2 MT::Asset::Image->class
693
694Returns 'image', the identifier for this particular class of asset.
695
696=head2 MT::Asset::Image->class_label
697
698Returns the localized descriptive name for this type of asset.
699
700=head2 MT::Asset::Image->extensions
701
702Returns an arrayref of file extensions that are supported by this
703package.
704
705=head2 $asset->metadata
706
707Returns a hashref of metadata values for this asset.
708
709=head2 $asset->thumbnail_file(%param)
710
711Creates or retrieves the file path to a thumbnail image appropriate for
712the asset. If a thumbnail cannot be created, this routine will return
713undef.
714
715=head2 $asset->as_html
716
717Return the HTML I<IMG> element with the image asset attributes.
718
719=head1 AUTHOR & COPYRIGHT
720
721Please see the L<MT/"AUTHOR & COPYRIGHT"> for author, copyright, and
722license information.
723
724=cut
Note: See TracBrowser for help on using the browser.