# Copyright 2002-2008 Josh Clark and Global Moxie, LLC. This code cannot be 
# redistributed without permission from globalmoxie.com.  For more
# information, consult your Big Medium license.
#
# $Id: Media.pm 3233 2008-08-21 12:47:26Z josh $

package BigMed::Media;
use strict;
use utf8;
use Carp;
use BigMed::Plugin;

use base qw(BigMed::Library);
use BigMed::DiskUtil (
    'bm_confirm_dir', 'bm_check_space', 'bm_copy_file', 'bm_file_path',
    'bm_move_file',   'bm_delete_file', 'bm_file_chmod'
);


my %media_types = ();

###########################################################
# SET MEDIA DATA SCHEMA
###########################################################

my @data_schema = (
    {   name  => 'title',
        type  => 'rich_text_inline',
        default => '',
        index => 1,
        unique => 1,
    },
);

BigMed::Media->register_minicontent(
    elements      => \@data_schema,
    can_embed     => 1,
    alpha_sort_col => 'title',
    lib_superclass => 1,
);

#override data's is_unique check to generate a unique title
sub is_unique {
    my $obj = shift;
    my $col = shift;
    return $obj->SUPER::is_unique($col) if $col ne 'title';
    while (!$obj->SUPER::is_unique('title')) {
        my $title = $obj->title;
        if ($title =~ /\-(\d+)$/) {
            my $suffix = $1;
            $suffix ++;
            $title =~ s/\-\d+$/\-$suffix/;
        }
        else {
            $title .= "-2";
        }
        $obj->set_title($title);
    }
    1;
}

###########################################################
# MEDIA TYPE MANAGEMENT
###########################################################

my $types_loaded;
sub _load_media {
    return 1 if $types_loaded;
    $types_loaded = 1;
    BigMed::Plugin->load_media_types();
}

sub register_minicontent {
    my $class = shift;
    $class->SUPER::register_minicontent(@_);
    $media_types{$class->data_label} = $class if $class ne "BigMed::Media";
}

sub media_types {
    _load_media();
    return sort { lc $a cmp lc $b } keys %media_types;
}

sub media_classes {
    _load_media();
    sort { lc $a cmp lc $b } values %media_types;
}

sub media_class {
    $_[1] or return undef;
    _load_media();
    $media_types{ $_[1] };
}

sub import_media_file {
    my ($self, %param) = @_;
    my ($dir, $ext_file, $use_file, $old) = @param{
        qw(dir ext_file filename replace)
    };
    croak 'external file path required' if !$ext_file;
    if (!-e $ext_file) {
        return BigMed::Error->set_error(
            head => 'MEDIA_Cannot Import Media File',
            text => ['MEDIA_The original file is missing', $ext_file],
        );
    }
    croak 'target dir required' if !$dir;
    bm_confirm_dir($dir, {build_path=>1}) or return;
    bm_check_space($dir, ( ( -s $ext_file ) + 100) ) or return;

    #if we're replacing an old file, okay to overwrite it;
    #if not, need to make sure we're using a unique filename
    $old ||= q{};
    if (!$use_file) {
        $use_file = $ext_file =~ m|\A.*[/\\](.+)\z|ms ? $1 : $ext_file;
    }
    my $old_path;
    my $filepath = bm_file_path($dir, $use_file);
    if ( lc $old ne lc $use_file ) {    #new filename; don't overwrite others
        #if there's a /[~.]s\d+x/ in there, insert digit before that, otherwise
        #before dot-suffix:
        #file~s200x200.jpg -> file-2~s200x200.jpg
        #file.jpg -> file-2.jpg

        my $dot = BigMed->bigmed->env('DOT');
        my $suffix =
            ( $use_file =~ /(\Q$dot\Es\d+x\d+.*\z)/ms ) ? $1
          : ( $use_file =~ /([.][a-zA-Z0-9]+)\z/ms ) ? $1
          : q{};
        my $basename = ( $use_file =~ /(.*)\Q$suffix\E\z/ ) ? $1 : $use_file;
        while ( -e $filepath ) {
            if ( $basename =~ /\-(\d+)$/ ) {
                my $num = $1 + 1;
                $basename =~ s/\-\d+$/\-$num/;
            }
            else {
                $basename .= "-2";
            }
            $use_file = "$basename$suffix";
            $filepath = bm_file_path( $dir, $use_file );
        }
    }
    elsif ($old && -e $filepath) { #same name, move out of harm's way
        $old .= '~bak';
        $old_path = bm_file_path( $dir, $old );
        bm_move_file( $filepath, $old_path, { build_path => 1 } ) or return;
    }

    #move the new file into place, but restore backup if trouble
    if ( !bm_copy_file( $ext_file, $filepath, { build_path => 1 } ) ) {
        bm_move_file( $old_path, $filepath ) if $old_path;
        return;
    }
    my $chmod = bm_file_chmod();
    chmod $chmod, $filepath;

    #delete the old file if there was one; hold off on errors.
    #make sure that we're not deleting the file we just copied (old_path
    #can equal filepath if the original file did not exist)
    $old_path = bm_file_path( $dir, $old ) if !$old_path && $old;
    bm_delete_file($old_path) if $old_path && $old_path ne $filepath;
    return $use_file;
}



1;

=head1 NAME

BigMed::Media - The base class for Big Medium media objects.

=head1 DESCRIPTION

As the base class for all Big Medium media objects, BigMed::Media is also
a registry for the types of media available in the Big Medium system. Media
objects are types of secondary content that can be attached or associated
with BigMed::Content objects. These objects belong to subclasses of BigMed::Media
and include images, documents, forms, and web service requests.

BigMed::Media is not itself intended to be used to store media information
but is instead an abstract class intended to be subclassed.

=head1 USAGE

BigMed::Media is a subclass of BigMed::Library (and thus of
BigMed::MiniContent), and so its subclasses are, too.
BigMed::Media subclasses should register themselves via the usual
BigMed::MiniContent C<register_minicontent> method. This will cause the
subclass to inherit the default BigMed::Media data columns, plus any new
custom data columns included in C<set_schema>'s C<elements> argument.

In addition to the methods  documented below, please see the BigMed::Data
documentation for details about topics including:

=over 4

=item Creating a new data object

=item Saving a data object

=item Finding and sorting saved data objects

=item Data access methods

=item Error handling

=back

=head1 METHODS

=head2 Data Access Methods

BigMed::Media bestows the following data columns on its subclasses. They can be
accessed and set using the standard data access methods described in the
BigMed::Data documentation. See the L<"Searching and Sorting"> section below
for details on the data columns available to search and sort BigMed::Site
objects.

=over 4

=item * id

The numeric ID of the pointer object

=item * site

The numeric ID of the site to which the two objects belong

=item * owner

The numeric ID of the user who "owns" the media object.

=item * shared

Boolean value indicating whether the owner allows this media to be used by other
users with privileges at the same site. (Admins and webmasters are always
allowed to use the media objects).

=item * in_lib

Boolean value indicating whether the object should be included in the library
display.

= item * slug

The slug name for the media object. This may be used to generate a filename
or be used as an id string in widgets. The slug must be unique within the
BigMed::Media subclass.

= item * title

The name of the media object.

= item * version

The version of the media object.

= item * description

A text description of the media object (this might be used as a caption for
some types of media).

=item * mod_time

Timestamp for when the last time the object was saved.

=item * create_time

Timestamp for when the object was first created.

=back

=head3 Searching and Sorting

You can look up and sort records by any combination of the following fields.
See the C<fetch> and C<select> documentation in BigMed::Data for more info.

=over 4

=item * id

=item * mod_time

=item * site

=item * owner

=item * shared

=item * in_lib

=item * slug

=item * title

=item * version

=back

=head2 Media Type Info

=over 4

=item * BigMed::Media->media_types

Returns an array of the label names of all BigMed::Media subclasses, in
alphabetical order.

=item * BigMed::Media->media_classes

Returns an array of the class names of all BigMed::Media subclasses.

=item * BigMed::Media->media_class('label_name')

Returns the class name associated with the label_name argument. Returns undef
if no such class exists.

=back

=head1 File Management

Media objects are associated with external media files (images, audio/video,
documents). The BigMed::Media class provides a method to import new files
into the site's media directories while protecting or replacing existing
files of the same name, as you specify.

=head2 C<< $class_or_obj->import_external_file(%param) >>

Imports (copies) an external file into a specified directory and
returns the relative filename of the file.

The method accepts a hash of parameters:

=over 4

=item * dir => $directory_path

B<Required.> The target directory into which you wish to import the file:

    dir => $site->doc_path

=item * ext_file => $file_path

B<Required.> The path to the file you wish to import.

=item * filename => $filename

Optional. The file name that you would like to use for the imported file. If
not provided, the original file name will be used from the C<ext_file>
parameter. If the filename already exists in the target directory and it
is not the same filename specified in the C<replace> parameter, then
a new filename will be selected to create a unique name. In other words,
no existing file will be replaced unless you specifically say so.
For example, if "foo.doc" already exists, the new file will be
named "foo-2.doc".

=item * replace => $existing_filename

Optional. If the imported file is intended to replace an existing file
in the target directory, specify that filename here:

    replace => $self->filename

The original file will be removed when the new file is imported.

=back

=head1 SEE ALSO

=over 4

=item * BigMed::Data

=item * BigMed::Library

=item * BigMed::MiniContent

=item * BigMed::Content

=back

=head1 AUTHOR & COPYRIGHTS

This module and all Big Medium modules are copyright Josh Clark
and Global Moxie. All rights reserved.

Use of this module and the Big Medium content
management system are governed by Global Moxie's software licenses
and may not be used outside of the terms and conditions outlined
there.

For more information, visit the Global Moxie website at
L<http://globalmoxie.com/>.

Big Medium and Global Moxie are service marks of Global Moxie
and Josh Clark. All rights reserved.

=cut


