Current File : //var/wcp4/hkaw/public_html/file/file/file/private/lib/GT/Template/Editor.pm
# ====================================================================
# Gossamer Threads Module Library - http://gossamer-threads.com/
#
#   GT::Template::Editor
#   Author: Alex Krohn
#   $Id: Editor.pm,v 2.4 2002/01/07 00:38:39 alex Exp $
#
# Copyright (c) 2001 Gossamer Threads Inc.  All Rights Reserved.
# ====================================================================
#
# Description:
#   A module for editing templates via an HTML browser.
#

package GT::Template::Editor;
# ===============================================================
    use strict;
    use GT::Base;
    use vars qw(@ISA $VERSION $DEBUG $ATTRIBS $ERRORS);
    @ISA     = qw/GT::Base/;
    $VERSION = sprintf "%d.%03d", q$Revision: 2.4 $ =~ /(\d+)\.(\d+)/;
    $DEBUG   = 0;
    $ATTRIBS = { cgi => undef, root => undef, backup => undef, default_dir => '', default_file => '', date_format => '' };
    $ERRORS  = {
        CANTOVERWRITE       => "Unable to overwrite file: %s (Permission Denied). Please set permissions properly and save again.",
        CANTCREATE          => "Unable to create new files in directory %s. Please set permissions properly and save again.",
        CANTMOVE            => "Unable to move file: %s to %s. Reason: %s",
        CANTMOVE            => "Unable to copy file: %s to %s. Reason: %s",
        FILECOPY            => "File::Copy is required in order to make backups.",
    };

sub process {
# ------------------------------------------------------------------
# Loads the template editor.
#
    my $self = shift;

    my $selected_dir  = $self->{cgi}->param('tpl_dir')  || $self->{default_dir} || 'default';
    my $selected_file = $self->{cgi}->param('tpl_file') || '';
    my $tpl_text      = '';
    my $error_msg     = '';
    my $success_msg   = '';
    my ($local, $restore) = (0, 0);

# Create the local directory if it doesn't exist.
    my $tpl_dir   = $self->{root} . '/' . $selected_dir;
    my $local_dir = $tpl_dir . "/local";
    if (! -d $local_dir) {
        mkdir ($local_dir, 0777) or return $self->error ('MKDIR', 'FATAL', $local_dir, "$!");
        chmod (0777, $local_dir);
    }
    my $dir = $local_dir;

# Perform a save if requested.
    if ($self->{cgi}->param('saveas') and my $file = $self->{cgi}->param('tpl_name')) {
        $tpl_text = $self->{cgi}->param('tpl_text'); 
        if (-e "$dir/$file" and ! -w _) {
            $error_msg = sprintf($ERRORS->{CANTOVERWRITE}, $file);
        }
        elsif (! -e _ and ! -w $dir) {
            $error_msg = sprintf($ERRORS->{CANTCREATE}, $dir); 
        }
        else {
            if ($self->{backup} and -e "$dir/$file") {
                $self->copy ("$dir/$file", "$dir/$file.bak")
            }
            local *FILE;
            open (FILE, "> $dir/$file") or return $self->error ('CANTOPEN', 'FATAL', "$dir/$file", "$!");
            $tpl_text =~ s/\r\n/\n/g;
            print FILE $tpl_text;
            close FILE;
            chmod 0666, "$dir/$file";
            $success_msg   = "File has been successfully saved.";
            $local         = 1;
            $restore       = 1 if -e "$self->{root}/$selected_dir/$file";
            $selected_file = $file;
            $tpl_text      = '';
        }
    }
# Delete a local template (thereby restoring the system template)
    elsif (my $restore = $self->{cgi}->param("restore")) {
        if ($self->{backup}) {
            if ($self->move("$dir/$restore", "$dir/$restore.bak")) {
                $success_msg = "System template '$restore' restored";
            }
            else {
                $error_msg = "Unable to restore system template '$restore': Cannot move '$dir/$restore': $!";
            }
        }
        else {
            if (unlink "$dir/$restore") {
                $success_msg = "System template '$restore' restored";
            }
            else {
                $error_msg = "Unable to remove $dir/$restore: $!";
            }
        }
    }
# Delete a local template (This is like restore, but happens when there is no system template)
    elsif (my $delete = $self->{cgi}->param("delete")) {        
        if ($self->{backup}) {
            if ($self->move("$dir/$delete", "$dir/$delete.bak")) {
                $success_msg = "Template '$delete' deleted";
            }
            else {
                $error_msg = "Unable to delete template '$delete': Cannot move '$dir/$delete': $!";
            }
        }
        else {
            if (unlink "$dir/$delete") {
                $success_msg = "Template '$delete' deleted";
            }
            else {
                $error_msg = "Unable to remove $dir/$delete: $!";
            }
        }
    }

# Load any selected template file.  
    if ($selected_file and ! $tpl_text) {
        if (-f "$dir/$selected_file") {
            local (*FILE, $/);
            open FILE, "$dir/$selected_file" or die "Unable to open file $dir/$selected_file: $!";
            $tpl_text = <FILE>;
            close FILE;
            $local = 1;
            $restore = 1 if -e "$self->{root}/$selected_dir/$selected_file";
        }
        elsif (-f "$self->{root}/$selected_dir/$selected_file") {
            local (*FILE, $/);
            open FILE, "$self->{root}/$selected_dir/$selected_file" or die "Unable to open file $self->{root}/$selected_dir/$selected_file: $!";
            $tpl_text = <FILE>;
            close FILE;
        }
        else {
            $selected_file = '';
        }
    }

# Load a README if it exists.
    my $readme;
    if (-e "$dir/README") {
        local (*FILE, $/);
        open FILE, "$dir/README" or die "unable to open readme: $dir/README ($!)";
        $readme = <FILE>;
        close FILE;
    }

# Set the textarea width and height.
    my $editor_rows = $self->{cgi}->param('cookie-editor_rows') || $self->{cgi}->cookie('editor_rows') || 15;
    my $editor_cols = $self->{cgi}->param('cookie-editor_cols') || $self->{cgi}->cookie('editor_cols') || 55;
    my $file_select = $self->template_file_select;
    my $dir_select  = $self->template_dir_select;
    $tpl_text = $self->{cgi}->html_escape($tpl_text);
    my $stats       = $selected_file ? $self->template_file_stats ($selected_file) : {};
    return {
        tpl_name    => $selected_file,
        tpl_file    => $selected_file,
        local       => $local,
        restore     => $restore,
        tpl_text    => \$tpl_text,
        error_message    => $error_msg,
        success_message  => $success_msg,
        tpl_dir     => $selected_dir,
        readme      => $readme,
        editor_rows => $editor_rows,
        editor_cols => $editor_cols,
        dir_select  => $dir_select,
        file_select => $file_select,
        %$stats
    };
}

sub template_file_select {
# ------------------------------------------------------------------
# Returns a select list of templates in a given dir.
#
    my $self = shift;
    my $path = $self->{root};
    my ($dir, $file, %files);
    my $selected_dir   = $self->{cgi}->param('tpl_dir')  || $self->{default_dir} || 'default';
    my $selected_file  = $self->{cgi}->param('tpl_file')  || $self->{default_file} || 'default';

    my $system_dir = $path . "/" . $selected_dir;
    my $local_dir  = $path . "/" . $selected_dir . '/local';
    foreach my $dir ($system_dir, $local_dir) {
        opendir (TPL, $dir) or next;
        while (defined ($file = readdir(TPL))) {
            next if ($file =~ /^\.\.?$/);
            next if ($file eq 'README');
            next if ($file eq 'languages.txt');
            next if ($file eq 'globals.txt');
            next if ($file =~ /\.bak/);
            next unless (-r "$dir/$file" and ! -d _);
            $files{$file} = 1;
        }
        closedir (TPL);
    }
    my $f_select_list = "<select name='tpl_file'>";
    foreach (sort keys %files) {
        my $changed = -e $path . '/' . $selected_dir . '/local/' . $_ ? ' *' : '';
        ($selected_file eq $_) ? ($f_select_list .= "<option value='$_' selected>$_$changed") : ($f_select_list .= "<option value='$_'>$_$changed");
    }
    $f_select_list .= "</select>";

    return $f_select_list;
}

sub template_dir_select {
# ------------------------------------------------------------------
# Returns a select list of template directories.
#
    my $self = shift;
    my ($dir, $file, @dirs);
    my $selected_dir = $self->{cgi}->param('tpl_dir')  || $self->{default_dir} || 'default';
    my $name         = 'tpl_dir';
    
    $dir = $self->{root};
    opendir (TPL, $dir) or die "unable to open directory: '$dir' ($!)";
    while (defined ($file = readdir(TPL))) {
        next if ($file =~ /^\.\.?$/);
        next if ($file eq 'admin' or $file eq 'help');
        next unless (-d "$dir/$file");
        push @dirs, $file;
    }
    closedir (TPL);

    my $d_select_list = "<select name='$name'>";
    foreach (sort @dirs) {
        ($selected_dir eq $_) ? ($d_select_list .= "<option selected>$_") : ($d_select_list .= "<option>$_");
    }
    $d_select_list .= "</select>";
    return $d_select_list;
}

sub template_file_stats {
# ------------------------------------------------------------------
# Returns information about a file. Takes the following arguments:
#   - filename
#   - template set
# The following tags are returned:
#   - file_path - the full path to the file, relative to the admin root directory
#   - file_size - the size of the file in bytes
#   - file_local - 1 or 0 - true if it is a local file
#   - file_restore - 1 or 0 - true if it is a local file and a non-local file of the same name exists (The non-local can be restored)
#   - file_mod_time - the date the file was last modified
#
    require GT::Date;
    my ($self, $file) = @_;
    my $tpl_dir       = $self->{cgi}->param('tpl_dir')  || $self->{default_dir} || 'default';    
    my $return = { file_local => 1, file_restore => 1 };
    my $dir = "$self->{root}/$tpl_dir";
    if (-f "$dir/local/$file" and -r _) {
        $return->{file_path} = "templates/$tpl_dir/local/$file";
        $return->{file_size} = -s _;
        $return->{file_local} = 1;
        $return->{file_restore} = (-f "$dir/$file" and -r _) ? 1 : 0;
        my $mod_time = (stat _)[9];
        if ($self->{date_format}) {
            require GT::Date;            
            $return->{file_mod_time} = GT::Date::date_get ($mod_time, $self->{date_format});
        }
        else {
            $return->{file_mod_time} = localtime($mod_time);
        }
    }
    else {
        $return->{file_path} = "templates/$tpl_dir/$file";
        $return->{file_size} = -s "$dir/$file";
        $return->{file_local} = 0;
        $return->{file_restore} = 0;
        my $mod_time = (stat _)[9];
        if ($self->{date_format}) {
            require GT::Date;            
            $return->{file_mod_time} = GT::Date::date_get ($mod_time, $self->{date_format});
        }
        else {
            $return->{file_mod_time} = localtime($mod_time);
        }
    }
    return $return;
}

sub move {
# -------------------------------------------------------------------
# Uses File::Copy to move a file.
#
    my $self = shift;
    my ($from, $to) = @_;
    eval { require File::Copy; };
    if ($@) {
        return $self->error ('FILECOPY', $@);
    }
    File::Copy::mv($from, $to) or return $self->error ('CANTMOVE', $from, $to, "$!");
}

sub copy {
# -------------------------------------------------------------------
# Uses File::Copy to move a file.
#
    my $self = shift;
    my ($from, $to) = @_;
    eval { require File::Copy; };
    if ($@) {
        return $self->error ('FILECOPY', $@);
    }
    File::Copy::cp($from, $to) or return $self->error ('CANTCOPY', $from, $to, "$!");
}

__END__

=head1 NAME

GT::Template::Editor - This module provides an easy way to edit templates.

=head1 SYNOPSIS

Should be called like:

    require GT::Template::Editor;
    my $editor = new GT::Template::Editor (
                    root        => $CFG->{admin_root_path} . '/templates',
                    default_dir => $CFG->{build_default_tpl},
                    backup      => 1,
                    cgi         => $IN
                );
    return $editor->process;

and it returns a hsah ref of variables used for displaying a template editor page.

=head1 COPYRIGHT

Copyright (c) 2001 Gossamer Threads Inc.  All Rights Reserved.
http://www.gossamer-threads.com/

=head1 VERSION

Revision: $Id: Editor.pm,v 2.4 2002/01/07 00:38:39 alex Exp $

=cut