root/branches/feature-narrow-tables/lib/MT/Asset.pm @ 1763

Revision 1763, 12.1 kB (checked in by mpaschal, 20 months ago)

Declare these meta objects as objects, until the upgrader can know them automatically
BugzID: 68749

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