root/branches/release-32/lib/MT/Asset.pm @ 1564

Revision 1564, 12.1 kB (checked in by bchoate, 20 months ago)

More index changes.

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;
8
9use strict;
10use MT::Tag; # Holds MT::Taggable
11use base qw( MT::Object MT::Taggable MT::Scorable );
12
13__PACKAGE__->install_properties({
14    column_defs => {
15        'id' => 'integer not null auto_increment',
16        'blog_id' => 'integer not null',
17        'label' => 'string(255)',
18        'url' => 'string(255)',
19        'description' => 'text',
20        'file_path' => 'string(255)',
21        'file_name' => 'string(255)',
22        'file_ext' => 'string(20)',
23        'mime_type' => 'string(255)',
24        'parent' => 'integer',
25    },
26    indexes => {
27        label => 1,
28        file_ext => 1,
29        parent => 1,
30        created_by => 1,
31        created_on => 1,
32        blog_class_date => {
33            columns => ['blog_id','class','created_on'],
34        },
35    },
36    class_type => 'file',
37    audit => 1,
38    meta => 1,
39    datasource => 'asset',
40    primary_key => 'id',
41});
42
43require MT::Asset::Image;
44require MT::Asset::Audio;
45require MT::Asset::Video;
46
47sub extensions {
48    undef;
49}
50
51# This property is a meta-property.
52sub file_path {
53    my $asset = shift;
54    my $path = $asset->SUPER::file_path(@_);
55    return $path if defined($path) && ($path !~ m!^\$!) && (-f $path);
56
57    $path = $asset->cache_property(sub {
58        my $path = $asset->SUPER::file_path();
59        if ($path && ($path =~ m!^\%([ras])!)) {
60            my $blog = $asset->blog;
61            my $root = !$blog || $1 eq 's' ? MT->instance->static_file_path
62                     : $1 eq 'r'           ? $blog->site_path
63                     :                       $blog->archive_path
64                     ;
65            $root =~ s!(/|\\)$!!;
66            $path =~ s!^\%[ras]!$root!;
67        }
68        $path;
69    }, @_);
70    return $path;
71}
72
73sub url {
74    my $asset = shift;
75    my $url = $asset->SUPER::url(@_);
76    return $url if defined($url) && ($url !~ m!^\%!) && ($url =~ m!^https://!);
77
78    $url = $asset->cache_property(sub {
79        my $url = $asset->SUPER::url();
80        if ($url =~ m!^\%([ras])!) {
81            my $blog = $asset->blog;
82            my $root = !$blog || $1 eq 's' ? MT->instance->static_path
83                     : $1 eq 'r'           ? $blog->site_url
84                     :                       $blog->archive_url
85                     ;
86            $root =~ s!/$!!;
87            $url =~ s!^\%[ras]!$root!;
88        }
89        return $url;
90    }, @_);
91    return $url;
92}
93
94# Returns a localized name for the asset type. For MT::Asset, this is simply
95# 'File'.
96sub class_label {
97    MT->translate('Asset');
98}
99
100sub class_label_plural {
101    MT->translate("Assets");
102}
103
104# Removes the asset, associated tags and related file.
105# TBD: Should we track and remove any generated thumbnail files here too?
106sub remove {
107    my $asset = shift;
108    if (ref $asset) {
109        my $blog = MT::Blog->load($asset->blog_id);
110        require MT::FileMgr;
111        my $fmgr = $blog ? $blog->file_mgr : MT::FileMgr->new('Local');
112        my $file = $asset->file_path;
113        $fmgr->delete($file);
114        $asset->remove_cached_files;
115
116        # remove children.
117        my $class = ref $asset;
118        my $iter = __PACKAGE__->load_iter({ parent => $asset->id, class => '*' });
119        while(my $a = $iter->()) {
120            $a->remove;
121        }
122
123        # Remove MT::ObjectAsset records
124        $class = MT->model('objectasset');
125        $iter = $class->load_iter({ asset_id => $asset->id });
126        while (my $o = $iter->()) {
127            $o->remove;
128        }
129    }
130
131    $asset->SUPER::remove(@_);
132}
133
134sub save {
135    my $asset = shift;
136    if (defined $asset->file_ext) {
137        $asset->file_ext(lc($asset->file_ext));
138    }
139
140    unless ($asset->SUPER::save(@_)) {
141        print STDERR "error during save: " . $asset->errstr . "\n";
142        die $asset->errstr;
143    }
144}
145
146sub remove_cached_files {
147    my $asset = shift;
148 
149    # remove any asset cache files that exist for this asset
150    my $blog = $asset->blog;
151    if ($asset->id && $blog) {
152        my $cache_dir = $asset->_make_cache_path;
153        if ($cache_dir) {
154            require MT::FileMgr;
155            my $fmgr = $blog->file_mgr || MT::FileMgr->new('Local');
156            if ($fmgr) {
157                my $basename = $asset->file_name;
158                my $ext = '.'.$asset->file_ext;
159                $basename =~ s/$ext$//;
160                my $cache_glob = File::Spec->catfile($cache_dir,
161                    $basename . '-thumb-*' . $ext);
162                my @files = glob($cache_glob);
163                foreach my $file (@files) {
164                    $fmgr->delete($file);
165                }
166            }
167        }
168    }
169    1;
170}
171
172sub blog {
173    my $asset = shift;
174    my $blog_id = $asset->blog_id or return undef;
175    return $asset->{__blog} if $blog_id && $asset->{__blog} && ($asset->{__blog}->id == $blog_id);
176    require MT::Blog;
177    return $asset->{__blog} = MT::Blog->load($blog_id)
178        or return $asset->error("Failed to load blog for file");
179}
180
181# Returns a true/false response based on whether the active package
182# has extensions registered that match the requested filename.
183sub can_handle {
184    my ($pkg, $filename) = @_;
185    # undef is returned from fileparse if the extension is not known.
186    require File::Basename;
187    my $ext = $pkg->extensions || [];
188    return (File::Basename::fileparse($filename, @$ext))[2] ? 1 : 0;
189}
190
191# Given a filename, returns an appropriate MT::Asset class to associate
192# with it. This lookup is based purely on file extension! If none can
193# be found, it returns MT::Asset.
194sub handler_for_file {
195    my $pkg = shift;
196    my ($filename) = @_;
197    my $types;
198    # special case to check for all registered classes, not just
199    # those that are subclasses of this package.
200    if ($pkg eq 'MT::Asset') {
201        $types = [ keys %{ $pkg->properties->{__type_to_class} || {} } ];
202    }
203    $types ||= $pkg->type_list;
204    if ($types) {
205        foreach my $type (@$types) {
206            my $this_pkg = $pkg->class_handler($type);
207            if ($this_pkg->can_handle($filename)) {
208                return $this_pkg;
209            }
210        }
211    }
212    __PACKAGE__;
213}
214
215sub type_list {
216    my $pkg = shift;
217    my $props = $pkg->properties;
218    my $col = $props->{class_column};
219    my $this_type = $props->{class_type};
220    my @classes = values %{ $props->{__class_to_type} };
221    @classes = grep { m/^\Q$this_type\E:/ } @classes;
222    push @classes, $this_type;
223    return \@classes;
224}
225
226sub metadata {
227    my $asset = shift;
228    return {
229        MT->translate("Tags") => MT::Tag->join(',', $asset->tags),
230        MT->translate("Description") => $asset->description,
231        MT->translate("Name") => $asset->label,
232        url => $asset->url,
233        MT->translate("URL") => $asset->url,
234        MT->translate("Location") => $asset->file_path,
235        name => $asset->file_name,
236        'class' => $asset->class,
237        ext => $asset->file_ext,
238        mime_type => $asset->mime_type,
239        # duration => $asset->duration,
240    };
241}
242
243sub has_thumbnail {
244    0;
245}
246
247sub thumbnail_file {
248    undef;
249}
250
251sub thumbnail_filename {
252    undef;
253}
254
255sub stock_icon_url {
256    undef;
257}
258
259sub thumbnail_url {
260    my $asset = shift;
261    my (%param) = @_;
262
263    require File::Basename;
264    if (my ($thumbnail_file, $w, $h) = $asset->thumbnail_file(@_)) {
265        return $asset->stock_icon_url(@_) if !defined $thumbnail_file;
266        my $file = File::Basename::basename($thumbnail_file);
267        my $asset_file_path = $asset->SUPER::file_path();
268        my $site_url;
269        my $blog = $asset->blog;
270        if (!$blog) {
271            $site_url = $param{Pseudo} ? '%s' : MT->instance->static_path;
272            $site_url .= '/' unless $site_url =~ m!/$!;
273            $site_url .= 'support/';
274        }
275        elsif ( $asset_file_path =~ m/^%a/ ) {
276            $site_url = $param{Pseudo} ? '%a' : $blog->archive_url;
277        }
278        else {
279            $site_url = $param{Pseudo} ? '%r' : $blog->site_url;
280        }
281
282        if ($file && $site_url) {
283            require MT::Util;
284            my $path = $param{Path};
285            if (!defined $path) {
286                $path = MT::Util::caturl(MT->config('AssetCacheDir'), unpack('A4A2', $asset->created_on));
287            } else {
288                require File::Spec;
289                my @path = File::Spec->splitdir($path);
290                $path = '';
291                for my $p (@path) {
292                    $path = MT::Util::caturl($path, $p);
293                }
294            }
295            $file =~ s/%([A-F0-9]{2})/chr(hex($1))/gei;
296            $site_url = MT::Util::caturl($site_url, $path, $file);
297            return ($site_url, $w, $h);
298        }
299    }
300
301    # Use a stock icon
302    return $asset->stock_icon_url(@_);
303}
304
305sub as_html {
306    my $asset = shift;
307    my ($param) = @_;
308    my $fname = $asset->file_name;
309    require MT::Util;
310    my $text = sprintf '<a href="%s">%s</a>',
311        MT::Util::encode_html($asset->url),
312        MT::Util::encode_html($fname);
313    return $asset->enclose($text);
314}
315
316sub enclose {
317    my $asset = shift;
318    my ($html) = @_;
319    my $id = $asset->id;
320    my $type = $asset->class;
321    return qq{<form mt:asset-id="$id" class="mt-enclosure mt-enclosure-$type" style="display: inline;">$html</form>};
322}
323
324# Return a HTML snippet of form options for inserting this asset
325# into a web page. Default behavior is no options.
326sub insert_options {
327    my $asset = shift;
328    my ($param) = @_;
329    return undef;
330}
331
332sub on_upload {
333    my $asset = shift;
334    my ($param) = @_;
335    1;
336}
337
338sub edit_template_param {
339    my $asset = shift;
340    my ($cb, $app, $param, $tmpl) = @_;
341    return;
342}
343
344sub set_values_from_query {
345    my $asset = shift;
346    my ($q) = @_;
347
348    # Set the known columns from the form, if they're set. Subclasses can
349    # opt out or decorate this behavior by overriding the method.
350    my $names = $asset->column_names;
351    my %values;
352    for my $field (@$names) {
353        $values{$field} = $q->param($field)
354            if defined $q->param($field);
355    }
356    $asset->set_values(\%values);
357
358    1;
359}
360
361# $pseudo parameter causes function to return '%r' as
362# root instead of blog site path
363sub _make_cache_path {
364    my $asset = shift;
365    my ($path, $pseudo) = @_;
366    my $blog = $asset->blog;
367
368    require File::Spec;
369    my $year_stamp = '';
370    my $month_stamp = '';
371    if  (!defined $path) {
372        $year_stamp = unpack 'A4', $asset->created_on;
373        $month_stamp = unpack 'x4 A2', $asset->created_on;
374        $path = MT->config('AssetCacheDir');
375    } else {
376        my $merge_path = '';
377        my @split = File::Spec->splitdir($path);
378        for my $p (@split) {
379            $merge_path = File::Spec->catfile($merge_path, $p);
380        }
381        $path = $merge_path if $merge_path;
382    }
383
384    my $asset_file_path = $asset->SUPER::file_path();
385    my $format;
386    my $root_path;
387    if ( !$blog ) {
388        $format = '%s';
389        $root_path = File::Spec->catdir(MT->instance->static_file_path, 'support');
390    }
391    elsif ( $asset_file_path =~ m/^%a/ ) {
392        $format = '%a';
393        $root_path = $blog->archive_path;
394    }
395    else {
396        $format = '%r';
397        $root_path = $blog->site_path;
398    }
399
400    my $real_cache_path = File::Spec->catdir($root_path, $path, $year_stamp,
401        $month_stamp);
402    if (!-d $real_cache_path) {
403        require MT::FileMgr;
404        my $fmgr = $blog ? $blog->file_mgr : MT::FileMgr->new('Local');
405        $fmgr->mkpath($real_cache_path) or return undef;
406    }
407
408    my $asset_cache_path = File::Spec->catdir(($pseudo ? $format : $root_path),
409        $path, $year_stamp, $month_stamp);
410    $asset_cache_path;
411}
412
4131;
414
415__END__
416
417=head1 NAME
418
419MT::Asset
420
421=head1 SYNOPSIS
422
423    use MT::Asset;
424
425    # Example
426
427=head1 DESCRIPTION
428
429This module provides an object definition for a file that is placed under
430MT's control for publishing.
431
432=head1 METHODS
433
434=head2 MT::Asset->new
435
436Constructs a new asset object. The base class is the generic asset object,
437which represents a generic file.
438
439=head2 MT::Asset->handler_for_file($filename)
440
441Returns a I<MT::Asset> package suitable for the filename given. This
442determination is typically made based on the file's extension.
443
444=head1 AUTHORS & COPYRIGHT
445
446Please see the I<MT> manpage for author, copyright, and license information.
447
448=cut
Note: See TracBrowser for help on using the browser.