1035 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			1035 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
# ==================================================================
 | 
						|
# Gossamer Links - enhanced directory management system
 | 
						|
#
 | 
						|
#   Website  : http://gossamer-threads.com/
 | 
						|
#   Support  : http://gossamer-threads.com/scripts/support/
 | 
						|
#   CVS Info : 087,071,086,086,085      
 | 
						|
#   Revision : $Id: Upgrade.pm,v 1.50 2009/05/11 05:57:45 brewt Exp $
 | 
						|
# 
 | 
						|
# Copyright (c) 2005 Gossamer Threads Inc.  All Rights Reserved.
 | 
						|
# Redistribution in part or in whole strictly prohibited. Please
 | 
						|
# see LICENSE file for full details.
 | 
						|
# ==================================================================
 | 
						|
#
 | 
						|
 | 
						|
package Links::Upgrade;
 | 
						|
 | 
						|
use strict;
 | 
						|
use vars qw/%VERSION_TREE @VERSION_HIDDEN/;
 | 
						|
use Carp;
 | 
						|
BEGIN {
 | 
						|
    # 1.01 below should be updated if this file depends on fixes/additions to GT::SQL::Upgrade
 | 
						|
    if (exists $INC{'GT/SQL/Upgrade.pm'} and $GT::SQL::Upgrade::VERSION < 1.01) {
 | 
						|
        delete $INC{'GT/SQL/Upgrade.pm'};
 | 
						|
    }
 | 
						|
}
 | 
						|
use GT::SQL::Upgrade;
 | 
						|
 | 
						|
# This has to be updated every release so that an upgrade can "walk" the tree
 | 
						|
# to find any upgrade code.
 | 
						|
%VERSION_TREE = (
 | 
						|
    '2.0.0' => '2.0.1',
 | 
						|
    '2.0.1' => '2.0.2',
 | 
						|
    '2.0.2' => '2.0.3',
 | 
						|
    '2.0.3' => '2.0.4',
 | 
						|
    '2.0.4' => '2.0.5',
 | 
						|
    '2.0.5' => '2.1.0',
 | 
						|
    '2.1.0' => '2.1.1',
 | 
						|
    '2.1.1' => '2.1.2',
 | 
						|
    '2.1.2' => '2.2.0',
 | 
						|
    '2.2.0' => '2.2.1',
 | 
						|
    '2.2.1' => '2.99.0',
 | 
						|
    '2.99.0' => '2.99.1',
 | 
						|
    '2.99.1' => '3.0.0',
 | 
						|
    '3.0.0' => '3.0.1',
 | 
						|
    '3.0.1' => '3.0.2',
 | 
						|
    '3.0.2' => '3.0.3',
 | 
						|
    '3.0.3' => '3.0.4',
 | 
						|
    '3.0.4' => '3.1.0',
 | 
						|
    '3.1.0' => '3.2.0',
 | 
						|
    '3.2.0' => '3.3.0',
 | 
						|
);
 | 
						|
 | 
						|
# These versions won't show up in the available upgrade list returned by
 | 
						|
# upgrades_available().
 | 
						|
@VERSION_HIDDEN = ('2.99.0', '2.99.1');
 | 
						|
 | 
						|
sub PERFORM ($$) { "\nPerforming " . (substr($_[0], 0, 1) == 2 ? "Links SQL" : "Gossamer Links") . " $_[0] -> $_[1] upgrades...\n" }
 | 
						|
sub DONE ($$) { (substr($_[0], 0, 1) == 2 ? "Links SQL" : "Gossamer Links") . " $_[0] -> $_[1] upgrades performed.\n\n" }
 | 
						|
 | 
						|
# In list context, returns a list of versions that are available to be upgraded
 | 
						|
# from.  In scalar context, returns a hash reference containing a
 | 
						|
# upgrades_available key with a value of an array reference containing these
 | 
						|
# versions (i.e. for use in a template).  Only versions from %VERSION_TREE are
 | 
						|
# included that actually have some upgrade code - in other words, 2.0.1 won't
 | 
						|
# be includeded because there is no actual 2.0.1 upgrade code.
 | 
						|
sub upgrades_available {
 | 
						|
    my %skip = map { $_ => 1 } @VERSION_HIDDEN;
 | 
						|
    my @avail =
 | 
						|
        map $_->[0],
 | 
						|
        sort { for my $i (1 .. (@$a > @$b ? @$a : @$b)) { my $c = $a->[$i] <=> $b->[$i]; return $c if $c } 0 }
 | 
						|
        map { ($skip{$_} or !__PACKAGE__->can_upgrade($_)) ? () : [$_, split /\./] }
 | 
						|
        keys %VERSION_TREE;
 | 
						|
    return wantarray ? @avail : { upgrades_available => \@avail };
 | 
						|
}
 | 
						|
 | 
						|
# Usage:
 | 
						|
# Links::Upgrade->upgrade(
 | 
						|
#     from => $version, # e.g.  2.2.1
 | 
						|
#     output => $coderef, # code reference will be called with any output
 | 
						|
#     config => $config, # config object or hash reference (may be changed by upgrade code)
 | 
						|
# );
 | 
						|
sub upgrade {
 | 
						|
    my $class = shift;
 | 
						|
    my %opts = @_;
 | 
						|
    for (qw/from config/) {
 | 
						|
        $opts{$_} or croak "Links::Upgrade->upgrade requires a '$_' option";
 | 
						|
    }
 | 
						|
 | 
						|
    my ($version) = $opts{from} =~ /(\d+\.\d+\.\d+)/;
 | 
						|
    $version or croak "Invalid version passed to Links::Upgrade->upgrade: '$opts{from}'";
 | 
						|
 | 
						|
    ref $opts{config} eq 'HASH' or UNIVERSAL::isa($opts{config}, 'GT::Config') or croak "Invalid 'config' value passed to Links::Upgrade->upgrade: '$opts{config}' not a hash reference or GT::Config object.";
 | 
						|
    not $opts{output} or ref $opts{output} eq 'CODE' or croak "Invalid 'output' value passed to Links::Upgrade->upgrade: '$opts{output}' not a code reference.";
 | 
						|
    $opts{output} ||= sub { print @_ };
 | 
						|
 | 
						|
    my $safety;
 | 
						|
    while ($version) {
 | 
						|
        if (my $sub = $class->can_upgrade($version)) {
 | 
						|
            $sub->($opts{output}, $opts{config});
 | 
						|
        }
 | 
						|
        $safety++ < 100 or croak "Internal upgrade error: $version => $VERSION_TREE{$version} appears to be recursing.";
 | 
						|
    }
 | 
						|
    continue { $version = $VERSION_TREE{$version} }
 | 
						|
 | 
						|
# Walk the version upgrade tree
 | 
						|
    1;
 | 
						|
}
 | 
						|
 | 
						|
# Takes a version, returns a code reference if the version -> next version
 | 
						|
# upgrade code exists, undef otherwise.
 | 
						|
sub can_upgrade {
 | 
						|
    my ($class, $from) = @_;
 | 
						|
    my $to = $VERSION_TREE{$from};
 | 
						|
    for ($from, $to) { y/./_/ }
 | 
						|
    $class->can("upgrade__${from}__$to");
 | 
						|
}
 | 
						|
 | 
						|
# Called from GT::Template with the version to upgrade from.  Returns either an
 | 
						|
# error tag or a message tag.
 | 
						|
sub browser_upgrade {
 | 
						|
    my ($from, $stream) = @_;
 | 
						|
    $from and $from =~ /\d\.\d+\.\d/ or return { error => 'Invalid upgrade version entered.' };
 | 
						|
    my $ret = '';
 | 
						|
    __PACKAGE__->upgrade(
 | 
						|
        from => $from,
 | 
						|
        config => $Links::CFG,
 | 
						|
        output => sub { if ($stream) { print @_ } else { $ret .= join '', @_ } }
 | 
						|
    );
 | 
						|
 | 
						|
    return {
 | 
						|
        upgrade_successful => 1,
 | 
						|
        upgrade_result => $ret
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
# Although not strictly upgrade-specific (you can force a tree rebuild) it is
 | 
						|
# here as it is primarily an upgrade feature.
 | 
						|
# Takes 2-3 arguments - an output subroutine, a GT::SQL object, and an optional
 | 
						|
# force value - if specified and true, a rebuild will be forced.
 | 
						|
sub create_cat_tree {
 | 
						|
    my ($out, $DB, $force) = @_;
 | 
						|
    $out ||= sub { print @_ };
 | 
						|
 | 
						|
    require GT::SQL::Tree;
 | 
						|
    require GT::SQL::Tree::Rebuild;
 | 
						|
    my $t = $DB->table('Category');
 | 
						|
    my %roots;
 | 
						|
    my $rebuild = GT::SQL::Tree::Rebuild->new(
 | 
						|
        table => $t,
 | 
						|
        order_by => 'Full_Name', # Ensure that parents come before children
 | 
						|
        cols => [qw/ID FatherID Full_Name/],
 | 
						|
        missing_root => sub {
 | 
						|
            my ($row, $table) = @_;
 | 
						|
            my ($id, $father) = @$row{qw/ID FatherID/};
 | 
						|
            if (!$father) {
 | 
						|
                return $roots{$id} = 0;
 | 
						|
            }
 | 
						|
            my $root;
 | 
						|
            if (exists $roots{$father}) {
 | 
						|
                return $roots{$id} = $roots{$father} || $father;
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                die "No parent category found for $row->{Full_Name}! Your Category table is corrupted.";
 | 
						|
            }
 | 
						|
        },
 | 
						|
        missing_depth => sub {
 | 
						|
            my ($row, $table) = @_;
 | 
						|
            my $full_name = $row->{Full_Name};
 | 
						|
            return $row->{Full_Name} =~ y|/||;
 | 
						|
        }
 | 
						|
    );
 | 
						|
 | 
						|
    my $e = $DB->editor('Category');
 | 
						|
    $out->("Adding Category tree...\n");
 | 
						|
    my $ret = $e->add_tree(father => "FatherID", root => "CatRoot", depth => "CatDepth", force => $force ? 'force' : 'check', rebuild => $rebuild);
 | 
						|
    $out->($ret ? "\tOkay!\n" : "\tAn error occured: $GT::SQL::error\n");
 | 
						|
    $ret;
 | 
						|
}
 | 
						|
 | 
						|
sub browser_cat_tree {
 | 
						|
 | 
						|
    my $stream = shift;
 | 
						|
    my $message;
 | 
						|
    my $okay = create_cat_tree(sub { if ($stream) { print @_ } else { for (@_) { $message .= $_ } } }, $Links::DB, 1);
 | 
						|
    return { browser_cat_tree_success => $okay, browser_cat_tree_message => $message };
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__3_2_0__3_3_0 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 3.2.0 to 3.3.0
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    $out->(PERFORM '3.2.0' => '3.3.0');
 | 
						|
 | 
						|
    import lib $cfg->{admin_root_path};
 | 
						|
    require GT::SQL;
 | 
						|
    require GT::SQL::Table;
 | 
						|
    $Links::DB = my $DB = GT::SQL->new($cfg->{admin_root_path} . '/defs');
 | 
						|
    require GT::Config;
 | 
						|
    my $lang = GT::Config->load("$cfg->{admin_root_path}/templates/admin/language.txt");
 | 
						|
 | 
						|
# Gossamer Links German fix
 | 
						|
    if ($cfg->{date_review_format} eq '%dd.%mm%.%yyyy% %HH%:%MM%') {
 | 
						|
        $cfg->{date_review_format} = '%dd%.%mm%.%yyyy% %HH%:%MM%';
 | 
						|
    }
 | 
						|
 | 
						|
# These options were split into two, so they should retain the same value as the original option
 | 
						|
    if ($cfg->{build_new_date_span_pages} ne $cfg->{build_span_pages}) {
 | 
						|
        $cfg->{build_new_date_span_pages} = $cfg->{build_span_pages};
 | 
						|
    }
 | 
						|
    if ($cfg->{email_review_add} ne $cfg->{email_add}) {
 | 
						|
        $cfg->{email_review_add} = $cfg->{email_add};
 | 
						|
    }
 | 
						|
 | 
						|
    $out->("Turning on build_format_compat...\n\tOkay!\n");
 | 
						|
    $cfg->{build_format_compat} = 2;
 | 
						|
 | 
						|
# Add new Reviews subclass
 | 
						|
    $out->("Adding Reviews subclass...\n");
 | 
						|
    my $t = $DB->table('Reviews');
 | 
						|
    $t->subclass(
 | 
						|
        table => { Reviews => "Links::Table::Reviews" }
 | 
						|
    );
 | 
						|
    $t->save_state();
 | 
						|
    $out->("\tOkay!\n");
 | 
						|
 | 
						|
    $out->(DONE '3.2.0' => '3.3.0');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__3_1_0__3_2_0 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 3.1.0 to 3.2.0
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    $out->(PERFORM '3.1.0' => '3.2.0');
 | 
						|
 | 
						|
    import lib $cfg->{admin_root_path};
 | 
						|
    require GT::SQL;
 | 
						|
    require GT::SQL::Table;
 | 
						|
    $Links::DB = my $DB = GT::SQL->new($cfg->{admin_root_path} . '/defs');
 | 
						|
    require GT::Config;
 | 
						|
    my $lang = GT::Config->load("$cfg->{admin_root_path}/templates/admin/language.txt");
 | 
						|
 | 
						|
    $out->("Updating PayPal postback check...\n");
 | 
						|
    for my $postback (@{$cfg->{payment}->{postback}}) {
 | 
						|
        next unless $postback->{method} eq 'PayPal';
 | 
						|
        $postback->{var} = 'txn_type';
 | 
						|
        last;
 | 
						|
    }
 | 
						|
    $out->("\tOkay!\n");
 | 
						|
 | 
						|
    $out->("Updating review e-mail settings...\n");
 | 
						|
    if ($cfg->{admin_email_review_add}) {
 | 
						|
        $cfg->{admin_email_review_add} = $cfg->{admin_email_add};
 | 
						|
    }
 | 
						|
    if ($cfg->{admin_email_review_mod}) {
 | 
						|
        $cfg->{admin_email_review_mod} = $cfg->{admin_email_mod};
 | 
						|
    }
 | 
						|
    $out->("\tOkay!\n");
 | 
						|
 | 
						|
    $out->("Removing old browser templates from admin template set...\n");
 | 
						|
    my $dir = "$cfg->{admin_root_path}/templates/admin";
 | 
						|
    opendir TPL, $dir or die "Could not open '$dir': $!";
 | 
						|
    while (defined(my $file = readdir TPL)) {
 | 
						|
        next unless -f "$dir/$file" and $file =~ /^browser.*\.html$/;
 | 
						|
        unlink "$dir/$file";
 | 
						|
    }
 | 
						|
    $out->("\tOkay!\n");
 | 
						|
 | 
						|
    add_column($out, $DB, Sessions => session_expires => { type => 'TINYINT', default => 1 });
 | 
						|
    add_column($out, $DB, Reviews => Review_ModifyDate => { type => 'DATETIME', not_null => 1, default => '0000-00-00 00:00:00', form_display => $lang->{prompt_Review_ModifyDate} });
 | 
						|
    alter_column($out, $DB, Reviews => Review_Date => { type => 'DATETIME', not_null => 1, form_display => $lang->{prompt_Review_Date} });
 | 
						|
 | 
						|
    $out->(DONE '3.1.0' => '3.2.0');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__3_0_4__3_1_0 {
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
# Upgrade from 3.0.4 to 3.0.5
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
 | 
						|
    $out->(PERFORM '3.0.4' => '3.1.0');
 | 
						|
 | 
						|
    import lib $cfg->{admin_root_path};
 | 
						|
    require GT::SQL;
 | 
						|
    require GT::SQL::Table;
 | 
						|
    $Links::DB = my $DB = GT::SQL->new($cfg->{admin_root_path} . '/defs');
 | 
						|
    require GT::Config;
 | 
						|
    my $lang = GT::Config->load("$cfg->{admin_root_path}/templates/admin/language.txt");
 | 
						|
 | 
						|
    drop_index($out, $DB, CatLinks => 'catlnndx');
 | 
						|
 | 
						|
    $out->("Scanning for and removing duplicate entries from CatLinks table...\n");
 | 
						|
    # Do some hackery to get a non-subclassed CatLinks table
 | 
						|
    #my $catlinks = $DB->table('CatLinks');
 | 
						|
    my $catlinks = GT::SQL::Table->new(
 | 
						|
        name => $DB->prefix . 'CatLinks',
 | 
						|
        connect => $DB->{connect},
 | 
						|
        debug => $DB->{_debug},
 | 
						|
        _err_pkg => 'GT::SQL::Table'
 | 
						|
    );
 | 
						|
    $catlinks->select_options('GROUP BY LinkID, CategoryID', 'HAVING COUNT(*) > 1');
 | 
						|
    my $sth = $catlinks->select(qw[LinkID CategoryID COUNT(*)]);
 | 
						|
    my $count;
 | 
						|
    while (my ($linkid, $catid) = $sth->fetchrow) {
 | 
						|
        my $deleted = $catlinks->delete({ LinkID => $linkid, CategoryID => $catid });
 | 
						|
        $count += $deleted - 1;
 | 
						|
        $catlinks->insert({ LinkID => $linkid, CategoryID => $catid });
 | 
						|
    }
 | 
						|
    $out->("\tOkay! " . ($count ? "$count duplicate entries found and removed.\n" : "No duplicate entries found.\n"));
 | 
						|
 | 
						|
    add_unique($out, $DB, CatLinks => { cl_cl_q => [qw/CategoryID LinkID/] });
 | 
						|
 | 
						|
 | 
						|
    if ($cfg->{updates}) {
 | 
						|
        $out->("Moving update data from Links/Config/Data.pm to Links/Config/Updates.pm\n");
 | 
						|
        my $cfg_updates = delete $cfg->{updates};
 | 
						|
        require GT::Config;
 | 
						|
        my $updates = GT::Config->load("$cfg->{admin_root_path}/Links/Config/Updates.pm", { create_ok => 1 });
 | 
						|
        for (keys %$cfg_updates) {
 | 
						|
            $updates->{$_} ||= $cfg_updates->{$_};
 | 
						|
        }
 | 
						|
        $updates->save;
 | 
						|
        $out->("\tOkay!\n");
 | 
						|
    }
 | 
						|
 | 
						|
    add_column($out, $DB, Links => LinkExpired => { type => 'INT', form_display => $lang->{prompt_LinkExpired}, form_type => 'hidden' });
 | 
						|
    add_index($out, $DB, Category => { c_p => ['Payment_Mode'] });
 | 
						|
 | 
						|
    $out->(DONE '3.0.4' => '3.1.0');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__3_0_3__3_0_4 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 3.0.3 to 3.0.4
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    $out->(PERFORM '3.0.3' => '3.0.4');
 | 
						|
    $out->(DONE '3.0.3' => '3.0.4');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__3_0_2__3_0_3 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 3.0.2 to 3.0.3
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    $out->(PERFORM '3.0.2' => '3.0.3');
 | 
						|
    $out->(DONE '3.0.2' => '3.0.3');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__3_0_1__3_0_2 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 3.0.1 to 3.0.2
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    $out->(PERFORM '3.0.1' => '3.0.2');
 | 
						|
    $out->(DONE '3.0.1' => '3.0.2');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__3_0_0__3_0_1 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 3.0.0 to 3.0.1
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    $out->(PERFORM '3.0.0' => '3.0.1');
 | 
						|
 | 
						|
    import lib $cfg->{admin_root_path};
 | 
						|
    require GT::SQL;
 | 
						|
    $Links::DB = my $DB = GT::SQL->new($cfg->{admin_root_path} . '/defs');
 | 
						|
 | 
						|
    recreate_table($out, $DB, 'MailingListIndex', sub { my $table = shift; my $cols = $table->cols; $cols->{Name}->{type} eq 'TEXT' },
 | 
						|
        cols => [
 | 
						|
            ID           => { type => 'INT', unsigned => 1, not_null => 1 },
 | 
						|
            Name         => { type => 'CHAR', size => 255, not_null => 1 },
 | 
						|
            DateModified => { type => 'INT', not_null => 1 },
 | 
						|
            DateCreated  => { type => 'INT', not_null => 1 }
 | 
						|
        ],
 | 
						|
        pk => 'ID',
 | 
						|
        ai => 'ID'
 | 
						|
    );
 | 
						|
 | 
						|
    if (-e(my $oldconfig = "$cfg->{admin_root_path}/Links/ConfigData.pm")) {
 | 
						|
        $out->("Removing old Links/ConfigData.pm file (has been replaced with Links/Config/Data.pm)...\n");
 | 
						|
        require GT::File::Tools;
 | 
						|
        my $ret = GT::File::Tools::move($oldconfig, "$oldconfig.old");
 | 
						|
        $out->($ret ? "\tOkay!\n" : "\tAn error occured: $!\n");
 | 
						|
    }
 | 
						|
 | 
						|
    $out->(DONE '3.0.0' => '3.0.1');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__2_99_1__3_0_0 {
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
# Placeholders that currently just prints a 2.2.1 -> 3.0.0 success message.  If
 | 
						|
# a 2.99.1 is released, this will become 2_99_1__3_0_0 and so on.  This allows
 | 
						|
# transparent handling of the beta versions without duplicating any code and
 | 
						|
# without needing to mention the beta in the upgrade output.
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
 | 
						|
    $out->("Updating build_static_path, _url...\n");
 | 
						|
    $cfg->{build_static_path} ||= "$cfg->{build_root_path}/static";
 | 
						|
    $cfg->{build_static_url} ||= "$cfg->{build_root_url}/static";
 | 
						|
    $out->("\tOkay!\n");
 | 
						|
 | 
						|
    $out->(DONE '2.2.1' => '3.0.0');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__2_99_0__2_99_1 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 2.99.0 to 2.99.1
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    import lib $cfg->{admin_root_path};
 | 
						|
    require GT::Config;
 | 
						|
    my $lang = GT::Config->load("$cfg->{admin_root_path}/templates/admin/language.txt");
 | 
						|
 | 
						|
    require GT::SQL;
 | 
						|
    $Links::DB = my $DB = GT::SQL->new("$cfg->{admin_root_path}/defs");
 | 
						|
 | 
						|
# Drop unnecessary Bookmark columns added to the Users table in 2.99.0
 | 
						|
    my $usercols = $DB->table('Users')->cols;
 | 
						|
    for (qw/FolderSortField FolderSortOrd/) {
 | 
						|
        drop_column($out, $DB, 'Users', $_) if exists $usercols->{$_};
 | 
						|
    }
 | 
						|
 | 
						|
    delete $cfg->{bookmark_folder_sort};
 | 
						|
    delete $cfg->{bookmark_folder_sort_order};
 | 
						|
    delete $cfg->{bookmark_user_sort};
 | 
						|
    delete $cfg->{bookmark_user_sort_order};
 | 
						|
 | 
						|
# Don't print here - the final 2.99.x -> 3.0.0 code prints the final message.
 | 
						|
    1;
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__2_2_1__2_99_0 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 2.2.1 to 2.99.0
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    $out->(PERFORM '2.2.1' => '3.0.0');
 | 
						|
    import lib $cfg->{admin_root_path};
 | 
						|
    require GT::Config;
 | 
						|
    my $lang = GT::Config->load("$cfg->{admin_root_path}/templates/admin/language.txt");
 | 
						|
 | 
						|
    require GT::SQL;
 | 
						|
    $Links::DB = my $DB = GT::SQL->new("$cfg->{admin_root_path}/defs");
 | 
						|
    $Links::STASH{clicktrack_cleanup} = 1;
 | 
						|
    $Links::STASH{expired_links} = 1;
 | 
						|
 | 
						|
    require Links::Plugins;
 | 
						|
    require GT::Plugins::Manager;
 | 
						|
    my $plugin = GT::Config->load("$cfg->{admin_root_path}/Plugins/plugin.cfg", { create_ok => 1 });
 | 
						|
    {
 | 
						|
        package FakeCGI;
 | 
						|
        sub param { $_[0]->{$_[1]} }
 | 
						|
    }
 | 
						|
 | 
						|
    add_column($out, $DB, Category => CatRoot => { type => 'INT', not_null => 1, unsigned => 1, default => 0, form_type => 'hidden' });
 | 
						|
    add_column($out, $DB, Category => CatDepth => { type => 'INT', not_null => 1, unsigned => 1, default => 0, form_type => 'hidden' });
 | 
						|
    add_column($out, $DB, Category => Direct_Links => { type => 'INT', not_null => 1, default => 0, form_display => $lang->{prompt_Direct_Links} });
 | 
						|
 | 
						|
    create_cat_tree($out, $DB);
 | 
						|
 | 
						|
    my $t = $DB->table('Category');
 | 
						|
    $out->("Updating Category fk to reference itself...\n");
 | 
						|
    my $ret = $t->fk(Category => { FatherID => 'ID' });
 | 
						|
    $out->($ret ? "\tOkay!\n" : "\tAn error occured: $GT::SQL::error\n");
 | 
						|
 | 
						|
    $out->("Updating CatLinks subclass...\n");
 | 
						|
    $t = $DB->table('CatLinks');
 | 
						|
    $t->subclass(
 | 
						|
        table => { CatLinks => "Links::Table::CatLinks" }
 | 
						|
    );
 | 
						|
    $t->save_state();
 | 
						|
    $out->("\tOkay!\n");
 | 
						|
 | 
						|
    $out->("Updating ClickTrack subclass...\n");
 | 
						|
    $t = $DB->table('ClickTrack');
 | 
						|
    $t->subclass(
 | 
						|
        table => { ClickTrack => "Links::Table::ClickTrack" }
 | 
						|
    );
 | 
						|
    $t->save_state();
 | 
						|
    $out->("\tOkay!\n");
 | 
						|
 | 
						|
    $out->("Updating Direct_Links values...\n");
 | 
						|
    my $rel = $DB->table(qw/CatLinks Links/);
 | 
						|
    $rel->select_options("GROUP BY CategoryID");
 | 
						|
    my $where = GT::SQL::Condition->new(isValidated => '=' => 'Yes');
 | 
						|
    $where->add(ExpiryDate => '>=' => time) if $cfg->{payment}->{enabled};
 | 
						|
    my %catlinks = $rel->select(qw/CategoryID COUNT(ID)/ => $where)->fetchall_list;
 | 
						|
    $t = $DB->table('Category');
 | 
						|
    for (keys %catlinks) {
 | 
						|
        $t->update({ Direct_Links => $catlinks{$_} }, { ID => $_ }, { GT_SQL_SKIP_CHECK => 1, GT_SQL_SKIP_INDEX => 1 });
 | 
						|
    }
 | 
						|
    $out->("\tOkay!\n");
 | 
						|
 | 
						|
    if ($plugin and exists $plugin->{Bookmark}) {
 | 
						|
        my $bcfg = Links::Plugins::get_plugin_user_cfg('Bookmark');
 | 
						|
        $out->("Bookmark plugin detected, importing Bookmark settings...\n");
 | 
						|
        $out->("\tImporting Bookmark configuration...\n");
 | 
						|
        for (keys %$bcfg) {
 | 
						|
            $cfg->{"bookmark_$_"} = $bcfg->{$_};
 | 
						|
        }
 | 
						|
        $out->("\t\tDone!\n");
 | 
						|
 | 
						|
        $out->("\tUninstalling Bookmark plugin...\n");
 | 
						|
        my $fakein = bless { plugin_name => "Bookmark", skip_uninstall => 1 }, "FakeCGI";
 | 
						|
        my $man = new GT::Plugins::Manager(cgi => $fakein, plugin_dir => "$cfg->{admin_root_path}/Plugins");
 | 
						|
        $ret = $man->uninstall;
 | 
						|
        $out->($ret->{error} ? "\t\tAn error occured: $ret->{error}\n" : "\t\tDone!\n");
 | 
						|
    }
 | 
						|
 | 
						|
    add_table($out, $DB, 'Bookmark_Folders',
 | 
						|
        cols => [
 | 
						|
            my_folder_id               => { type => 'INT', not_null => 1, unsigned => 1 },
 | 
						|
            my_folder_name             => { type => 'VARCHAR', not_null => 1, size => 255 },
 | 
						|
            my_folder_description      => { type => 'VARCHAR', size => 255 },
 | 
						|
            my_folder_user_username_fk => { type => 'VARCHAR', size => 50 },
 | 
						|
            my_folder_default          => { type => 'TINYINT', not_null => 1, default => 0, unsigned => 1 },
 | 
						|
            my_folder_public           => { type => 'TINYINT', not_null => 1, default => 0, unsigned => 1 }
 | 
						|
        ],
 | 
						|
        pk => 'my_folder_id',
 | 
						|
        ai => 'my_folder_id',
 | 
						|
        fk => {
 | 
						|
            Users => { my_folder_user_username_fk => 'Username' }
 | 
						|
        }
 | 
						|
    );
 | 
						|
 | 
						|
    add_table($out, $DB, 'Bookmark_Links',
 | 
						|
        cols => [
 | 
						|
            my_id               => { type => 'INT', not_null => 1, unsigned => 1 },
 | 
						|
            my_link_id_fk       => { type => 'INT', not_null => 1, unsigned => 1 },
 | 
						|
            my_user_username_fk => { type => 'VARCHAR', size  => 50 },
 | 
						|
            my_folder_id_fk     => { type => 'INT', not_null => 1, unsigned => 1 },
 | 
						|
            my_comment          => { type => 'VARCHAR', size  => '255' }
 | 
						|
        ],
 | 
						|
        pk => 'my_id',
 | 
						|
        ai => 'my_id',
 | 
						|
        fk => {
 | 
						|
            Users            => { my_user_username_fk => 'Username' },
 | 
						|
            Bookmark_Folders => { my_folder_id_fk => 'my_folder_id' },
 | 
						|
            Links            => { my_link_id_fk => 'ID' },
 | 
						|
        }
 | 
						|
    );
 | 
						|
 | 
						|
# Commented out columns were removed in 2.99.1
 | 
						|
#    add_column($out, $DB, Users => FolderSortField => { type => 'VARCHAR', size => 255, not_null => 1, regex => '^[\s\w]+$', default => 'my_folder_name', form_display => $lang->{prompt_FolderSortField} });
 | 
						|
#    add_column($out, $DB, Users => FolderSortOrd => { type => 'ENUM', values => ['ASC', 'DESC'], not_null => 1, default => 'ASC', form_display => $lang->{prompt_FolderSortOrd} });
 | 
						|
    add_column($out, $DB, Users => SortField => { type => 'VARCHAR', size => 255, not_null => 1, regex => '^[\s\w]+$', default => 'Title', form_display => $lang->{prompt_SortField} });
 | 
						|
    add_column($out, $DB, Users => SortOrd => { type => 'ENUM', values => ['ASC', 'DESC'], not_null => 1, default => 'ASC', form_display => $lang->{prompt_SortOrd} });
 | 
						|
    add_column($out, $DB, Users => PerPage => { type => 'INT', not_null => 1, unsigned => 1, default => 15, form_display => $lang->{prompt_PerPage} });
 | 
						|
    add_column($out, $DB, Users => Grouping => { type => 'TINYINT', not_null => 1, unsigned => 1, default => 0, form_display => $lang->{prompt_Grouping} });
 | 
						|
 | 
						|
    add_column($out, $DB, Editors => CanModReview => { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No' });
 | 
						|
 | 
						|
# Integrate the user's email template changes into the new templates
 | 
						|
    $out->("Upgrading email templates...\n");
 | 
						|
    require GT::File::Tools;
 | 
						|
    require GT::Mail;
 | 
						|
    require GT::Mail::Parse;
 | 
						|
    my %files = (
 | 
						|
        'email-add.txt' => {
 | 
						|
            new_name => 'link_added.eml',
 | 
						|
            subject  => 'VAL_APPROVESUB',
 | 
						|
        },
 | 
						|
        'email-del.txt' => {
 | 
						|
            new_name => 'link_rejected.eml',
 | 
						|
            subject  => 'VAL_REJECTSUB',
 | 
						|
        },
 | 
						|
        'email-mod.txt' => {
 | 
						|
            new_name => 'link_modified.eml',
 | 
						|
            subject  => 'VAL_APPROVECHGSUB',
 | 
						|
        },
 | 
						|
        'email-notify.txt' => {
 | 
						|
            new_name => 'link_expiry_notify.eml',
 | 
						|
            subject  => 'LINKS_NOTIFY_SUBJECT',
 | 
						|
        },
 | 
						|
        'email-expired.txt' => {
 | 
						|
            new_name => 'link_expired.eml',
 | 
						|
            subject  => 'LINKS_NOTIFY_SUBJECT',
 | 
						|
        },
 | 
						|
        'email-password.txt' => {
 | 
						|
            new_name => 'password.eml',
 | 
						|
            subject  => 'USER_LOSTPASSSUB',
 | 
						|
        },
 | 
						|
        'email-validate.txt' => {
 | 
						|
            new_name => 'validate.eml',
 | 
						|
            subject  => 'USER_VALEMAILSUB',
 | 
						|
        },
 | 
						|
        'review-email-add.txt' => {
 | 
						|
            new_name => 'review_added.eml',
 | 
						|
            subject  => 'REVIEW_VAL_APPROVESUB',
 | 
						|
        },
 | 
						|
        'review-email-del.txt' => {
 | 
						|
            new_name => 'review_rejected.eml',
 | 
						|
            subject  => 'VAL_REJECTSUB',
 | 
						|
        }
 | 
						|
    );
 | 
						|
    my $new_template = 'luna';
 | 
						|
    my $template_path = "$cfg->{admin_root_path}/templates";
 | 
						|
    my $fh = \do { local *FH; *FH };
 | 
						|
    opendir $fh, $template_path or die "Could not open '$template_path': $!";
 | 
						|
    while (my $template_set = readdir $fh) {
 | 
						|
        next if $template_set =~ /^\./ or $template_set eq 'admin' or $template_set eq $new_template
 | 
						|
            or $template_set =~ /_php$/ or $template_set =~ /^lang_.*\./ or not -d "$template_path/$template_set"
 | 
						|
            or $template_set eq 'browser' or $template_set eq 'CVS';
 | 
						|
        $out->("\tUpgrading $template_set template set...\n");
 | 
						|
        my $l = GT::Template::Inheritance->get_path(file => "language.txt", path => "$template_path/$template_set", use_local => 1, use_inheritance => 1);
 | 
						|
        unless (-e $l) {
 | 
						|
            $out->("\t\t(no language.txt found, not a template set?)\n");
 | 
						|
            next;
 | 
						|
        }
 | 
						|
        my $clang = GT::Config->load($l);
 | 
						|
        for my $file (keys %files) {
 | 
						|
            if (not -e "$template_path/$template_set/$files{$file}->{new_name}" and -e "$template_path/$new_template/$files{$file}->{new_name}") {
 | 
						|
                GT::File::Tools::copy("$template_path/$new_template/$files{$file}->{new_name}", "$template_path/$template_set/$files{$file}->{new_name}");
 | 
						|
            }
 | 
						|
            next unless -e "$template_path/$template_set/local/$file" and -r _ and not -e "$template_path/$template_set/local/$files{$file}->{new_name}";
 | 
						|
            $out->("\t\tCreating $files{$file}->{new_name} from $file... ");
 | 
						|
            open BODY, "$template_path/$template_set/local/$file" or die "Couldn't open template $template_path/$template_set/local/$file: $!";
 | 
						|
            my $body;
 | 
						|
            {
 | 
						|
                local $/;
 | 
						|
                $body = <BODY>;
 | 
						|
            }
 | 
						|
            close BODY;
 | 
						|
 | 
						|
            next unless -e "$template_path/$new_template/$files{$file}->{new_name}";
 | 
						|
            my $top = GT::Mail::Parse->new(
 | 
						|
                in_file        => "$template_path/$new_template/$files{$file}->{new_name}",
 | 
						|
                crlf           => "\n",
 | 
						|
                headers_intact => 0
 | 
						|
            )->parse();
 | 
						|
 | 
						|
            $top->body_data($body) if $body;
 | 
						|
            $top->set(Subject => $clang->{$files{$file}->{subject}}) if $clang->{$files{$file}->{subject}};
 | 
						|
 | 
						|
            if ($body or $clang->{$files{$file}->{subject}}) {
 | 
						|
                my $mail = new GT::Mail;
 | 
						|
                $mail->top_part($top);
 | 
						|
                $mail->write("$template_path/$template_set/local/$files{$file}->{new_name}");
 | 
						|
            }
 | 
						|
            $out->("done!\n");
 | 
						|
# We could also delete the subject language keys and the old email templates,
 | 
						|
# but it's better we leave them around than delete something they might not
 | 
						|
# want to lose or if something else is still using them.
 | 
						|
        }
 | 
						|
        $out->("\t\tOkay!\n");
 | 
						|
    }
 | 
						|
    closedir $fh;
 | 
						|
    $out->("\tOkay!\n");
 | 
						|
 | 
						|
    if (delete $cfg->{foreign_char}) {
 | 
						|
        $cfg->{build_category_format} = '%Full_ID%';
 | 
						|
        $cfg->{build_category_dynamic} = 'ID';
 | 
						|
    }
 | 
						|
    elsif (my $f = delete $cfg->{build_directory_field}) {
 | 
						|
        $cfg->{build_category_format} = $f ? "%$f%" : '';
 | 
						|
        $cfg->{build_category_dynamic} = $f;
 | 
						|
    }
 | 
						|
 | 
						|
    add_table($out, $DB, 'SearchLogs',
 | 
						|
        cols => [
 | 
						|
            slog_query => { type => 'VARCHAR', not_null => 1, size => 255 },
 | 
						|
            slog_count => { type => 'INT', not_null => 1, default => 0 },
 | 
						|
            slog_hits  => { type => 'INT', not_null => 1, default => 0 },
 | 
						|
            slog_time  => { type => 'FLOAT' },
 | 
						|
            slog_last  => { type => 'INT', not_null => 1, default => 0 },
 | 
						|
        ],
 | 
						|
        pk => 'slog_query'
 | 
						|
    );
 | 
						|
 | 
						|
    if ($plugin and exists $plugin->{SearchLogger} and $DB->table('SearchLog')) {
 | 
						|
        $out->("SearchLogger plugin detected, importing SearchLogger settings...\n");
 | 
						|
        my $old = $DB->table('SearchLog');
 | 
						|
        my $new = $DB->table('SearchLogs');
 | 
						|
        require GT::Date;
 | 
						|
        $out->("\tTransferring old search logs...\n");
 | 
						|
        my $sth = $old->select(qw/Term HitCount Results Last_Hit/);
 | 
						|
        my $i;
 | 
						|
        if ($sth) {
 | 
						|
            while (my $row = $sth->fetchrow_hashref) {
 | 
						|
                $i++;
 | 
						|
                my %slog_row = (
 | 
						|
                    slog_query => $row->{Term},
 | 
						|
                    slog_count => $row->{HitCount},
 | 
						|
                    slog_hits  => $row->{Results},
 | 
						|
                    slog_time  => undef
 | 
						|
                );
 | 
						|
                my @time;
 | 
						|
                if ($row->{Last_Hit} =~ /^(\d{4}-\d\d-\d\d ( ?)\d?\d:\d\d:\d\d)/) {
 | 
						|
                    @time = GT::Date::parse_format($1, "%yyyy%-%mm%-%dd% $2%H%:%MM%:%ss%");
 | 
						|
                }
 | 
						|
                elsif ($row->{Last_Hit} =~ /^(\d{4}-\d\d-\d\d)/) {
 | 
						|
                    @time = GT::Date::parse_format($1, '%yyyy%-%mm%-%dd%');
 | 
						|
                }
 | 
						|
                $slog_row{slog_last} = @time ? GT::Date::timelocal(@time) : 0;
 | 
						|
                $ret = $new->insert(\%slog_row);
 | 
						|
                $out->("\t\tAn error occured: $GT::SQL::error\n") unless $ret;
 | 
						|
            }
 | 
						|
            $out->("\t\t$i rows imported.\n");
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            $out->("\t\tAn error occured: $GT::SQL::error\n");
 | 
						|
        }
 | 
						|
 | 
						|
        $out->("\tDropping SearchLog table...\n");
 | 
						|
        my $e = $DB->editor('SearchLog');
 | 
						|
        my $ret = $e->drop_table;
 | 
						|
        $out->($ret ? "\t\tOkay!\n" : "\t\tAn error occured: $GT::SQL::error\n");
 | 
						|
 | 
						|
        $out->("\tUninstalling SearchLogger plugin...\n");
 | 
						|
        my $fakein = bless { plugin_name => "SearchLogger", skip_uninstall => 1 }, "FakeCGI";
 | 
						|
        my $man = new GT::Plugins::Manager(cgi => $fakein, plugin_dir => "$cfg->{admin_root_path}/Plugins");
 | 
						|
        $ret = $man->uninstall;
 | 
						|
        $out->("\t\tOkay!\n");
 | 
						|
    }
 | 
						|
 | 
						|
    add_table($out, $DB, 'NewsletterSubscription',
 | 
						|
        cols => [
 | 
						|
            UserID     => { type => 'CHAR', size => 50 },
 | 
						|
            CategoryID => { type => 'INT', not_null => 1 },
 | 
						|
        ],
 | 
						|
        unique => {
 | 
						|
            ns_uc => ['UserID', 'CategoryID']
 | 
						|
        },
 | 
						|
        fk => {
 | 
						|
            Users    => { UserID => 'Username' },
 | 
						|
            Category => { CategoryID => 'ID' }
 | 
						|
        }
 | 
						|
    );
 | 
						|
 | 
						|
    if (exists $DB->table('Users')->cols->{Newsletter}) {
 | 
						|
        $out->("Importing User Newsletter settings...\n");
 | 
						|
        my $sth = $DB->table('Users')->select('Username', { Newsletter => 'Yes' });
 | 
						|
        my $ns = $DB->table('NewsletterSubscription');
 | 
						|
        if ($sth) {
 | 
						|
            while (my $user = $sth->fetchrow) {
 | 
						|
                $ns->insert({ UserID => $user, CategoryID => 0 });
 | 
						|
            }
 | 
						|
            $out->("\tOkay!\n");
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            $out->("\tAn error occured: $GT::SQL::error\n");
 | 
						|
        }
 | 
						|
 | 
						|
        drop_column($out, $DB, 'Users', 'Newsletter');
 | 
						|
    }
 | 
						|
 | 
						|
# Don't print here - the final 2.99.x -> 3.0.0 code prints the final message.
 | 
						|
#    $out->("Links SQL 2.2.1 -> 3.0.0 upgrades performed.\n");
 | 
						|
    1;
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__2_2_0__2_2_1 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 2.2.0 to 2.2.1
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    $out->(PERFORM '2.2.0' => '2.2.1');
 | 
						|
 | 
						|
    import lib $cfg->{admin_root_path};
 | 
						|
    require GT::SQL;
 | 
						|
    $Links::DB = my $DB = GT::SQL->new($cfg->{admin_root_path} . '/defs');
 | 
						|
    $Links::STASH{expired_links} = 1;
 | 
						|
 | 
						|
    require GT::Config;
 | 
						|
    my $lang = GT::Config->load($cfg->{admin_root_path} . '/templates/admin/language.txt');
 | 
						|
 | 
						|
# Update Rating regex
 | 
						|
    alter_column($out, $DB, Links => Rating => { type => 'DECIMAL', precision => 4, scale => 2, not_null => 1, default => 0, regex => '^(?:10(?:\.0*)?|\d(?:\.\d*)?)$', form_display => $lang->{prompt_Rating} });
 | 
						|
 | 
						|
# Update payments_term from CHAR(8) to CHAR(10)
 | 
						|
    alter_column($out, $DB, Payments => payments_term => { type => 'CHAR', not_null => 1, size => 10 });
 | 
						|
 | 
						|
# Fix fk_tables that might have been deleted due to the SQL database overwrite bug
 | 
						|
    my %fk_tables = (
 | 
						|
        Category => [qw/CatPrice CatLinks CatRelations Editors/],
 | 
						|
        Links    => [qw/Payments Changes Reviews CatLinks Verify/],
 | 
						|
        Payments => [qw/PaymentLogs/],
 | 
						|
        Users    => [qw/Links Changes Reviews Editors Sessions/]
 | 
						|
    );
 | 
						|
    $out->("Checking fk_tables...\n");
 | 
						|
    my $p = $DB->prefix;
 | 
						|
    while (my ($table, $tables) = each %fk_tables) {
 | 
						|
        my $tb = $DB->table($table);
 | 
						|
        my $changed;
 | 
						|
        for (@$tables) {
 | 
						|
            $tb->_add_fk_table("$p$_") and $changed++;
 | 
						|
        }
 | 
						|
        if ($changed) {
 | 
						|
            $tb->save_state;
 | 
						|
            $out->("\t\t$table table's fk_tables repaired\n");
 | 
						|
        }
 | 
						|
    }
 | 
						|
    $out->("\tOkay!\n");
 | 
						|
 | 
						|
    $out->(DONE '2.2.0' => '2.2.1');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__2_1_2__2_2_0 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 2.1.2 to 2.2.0
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    $out->(PERFORM '2.1.2' => '2.2.0');
 | 
						|
 | 
						|
    import lib $cfg->{admin_root_path};
 | 
						|
    require GT::Config;
 | 
						|
 | 
						|
    # Check to see that the PPC plugin <1.93 is not installed.  Versions prior
 | 
						|
    # to 1.93 conflict with Links SQL's 'Payments' table.
 | 
						|
    my $plugin_cfg = GT::Config->load("$cfg->{admin_root_path}/Plugins/plugin.cfg");
 | 
						|
    if (exists $plugin_cfg->{PPC} and (!$plugin_cfg->{PPC}->{version} or $plugin_cfg->{PPC}->{version} < 1.93)) {
 | 
						|
        $out->("Old PPC plugin detected - you must upgrade the PPC plugin to 1.93 or above before upgrading Links SQL.");
 | 
						|
        die "Old PPC plugin detected - you must upgrade the PPC plugin to 1.93 or above before upgrading Links SQL.";
 | 
						|
    }
 | 
						|
 | 
						|
    require GT::SQL;
 | 
						|
    $Links::DB = my $DB = GT::SQL->new($cfg->{admin_root_path} . '/defs');
 | 
						|
    $Links::STASH{clicktrack_cleanup} = 1;
 | 
						|
    $Links::STASH{expired_links} = 1;
 | 
						|
 | 
						|
    my $lang = GT::Config->load("$cfg->{admin_root_path}/templates/admin/language.txt");
 | 
						|
 | 
						|
    for my $table (qw/Users Links Category/) {
 | 
						|
        $out->("Updating $table subclasses...\n");
 | 
						|
        # Create a new GT::SQL::Table object manually as I do _not_ want to
 | 
						|
        # load the existing subclasses.
 | 
						|
        my $t = GT::SQL::Table->new(
 | 
						|
            name     => "$DB->{connect}->{PREFIX}$table",
 | 
						|
            connect  => $DB->{connect},
 | 
						|
            debug    => $DB->{_debug},
 | 
						|
            _err_pkg => 'GT::SQL::Table'
 | 
						|
        );
 | 
						|
        $t->subclass(
 | 
						|
            table => { $table => "Links::Table::$table" },
 | 
						|
            html  => { $table => "Links::HTML::$table" }
 | 
						|
        );
 | 
						|
        $t->save_state();
 | 
						|
        $out->("\tOkay!\n");
 | 
						|
    }
 | 
						|
 | 
						|
    add_column($out, $DB, Category => Payment_Mode => { type => 'TINYINT', not_null => 1, default => 0, form_size => 1, form_names => [0,1,2,3], form_values => ['Use global settings','Not accepted','Optional','Required'], form_type => 'SELECT', form_display => $lang->{prompt_Payment_Mode} });
 | 
						|
    add_column($out, $DB, Category => Payment_Description => { type => 'TEXT', form_display => $lang->{prompt_Payment_Description} });
 | 
						|
    add_column($out, $DB, Links => ExpiryDate => { type => 'INT', not_null => 1, default => 0x7fff_ffff, form_display => $lang->{prompt_ExpiryDate}, form_size => 35 });
 | 
						|
    add_column($out, $DB, Links => ExpiryCounted => { type => 'TINYINT', not_null => 1, default => 0, form_display => $lang->{prompt_ExpiryCounted}, form_type => 'hidden' });
 | 
						|
    add_column($out, $DB, Links => ExpiryNotify  => { type => 'TINYINT', not_null => 1, default => 0, form_display => $lang->{prompt_ExpiryNotify},  form_type => 'hidden' });
 | 
						|
    drop_index($out, $DB, Links => 'valndx');
 | 
						|
    add_index($out, $DB, Links => {
 | 
						|
        valexpndx => [qw/isValidated ExpiryDate/],
 | 
						|
        expiryndx => [qw/ExpiryDate ExpiryNotify/],
 | 
						|
        expcntndx => [qw/ExpiryCounted ExpiryDate/]
 | 
						|
    });
 | 
						|
 | 
						|
    add_table($out, $DB, "CatPrice",
 | 
						|
        cols => [
 | 
						|
            cp_id          => { type => 'INT', not_null => 1, unsigned => 1 },
 | 
						|
            cp_cat_id_fk   => { type => 'INT', not_null => 1, unsigned => 1 },
 | 
						|
            cp_term        => { type => 'CHAR', not_null => 1, size => 10 }, # e.g. 8d, 1m, 2y, 3w, unlimited, etc.
 | 
						|
            cp_cost        => { type => 'FLOAT', not_null => 1 },
 | 
						|
            cp_type        => { type => 'TINYINT', not_null => 1, unsigned => 1 }, # 0 = signup, 1 = renewal, 2 = recurring
 | 
						|
            cp_description => { type => 'TEXT' }
 | 
						|
        ],
 | 
						|
        pk => 'cp_id',
 | 
						|
        ai => 'cp_id',
 | 
						|
        fk => { Category => { cp_cat_id_fk => 'ID' } }
 | 
						|
    );
 | 
						|
 | 
						|
    add_table($out, $DB, "Payments",
 | 
						|
        cols => [
 | 
						|
            payments_id     => { type => 'CHAR', not_null => 1, size => 16 },
 | 
						|
            payments_linkid => { type => 'INT', unsigned => 1, not_null => 1 },
 | 
						|
            payments_status => { type => 'INT', not_null => 1, default => 0, unsigned => 1 }, # 0 = pending, 1 = completed, 2 = declined, 3 = error
 | 
						|
            payments_method => { type => 'CHAR', not_null => 1, size => 25 },
 | 
						|
            payments_type   => { type => 'TINYINT', not_null => 1, unsigned => 1 }, # 0 = initial payment, 1 = renewal payment, 2 = recurring payment
 | 
						|
            payments_amount => { type => 'FLOAT', not_null => 1 },
 | 
						|
            payments_term   => { type => 'CHAR', not_null => 1, size => 8 }, # e.g. 8d, 1m, 2y, 3w, etc.
 | 
						|
            payments_start  => { type => 'INT', not_null => 1, unsigned => 1 },
 | 
						|
            payments_last   => { type => 'INT', not_null => 1, unsigned => 1 },
 | 
						|
        ],
 | 
						|
        pk => 'payments_id',
 | 
						|
        fk => { Links => { payments_linkid => 'ID' } },
 | 
						|
        index => {
 | 
						|
            p_sl => ['payments_status', 'payments_last'],
 | 
						|
            p_ll => ['payments_linkid', 'payments_last'],
 | 
						|
            p_al => ['payments_amount', 'payments_last'],
 | 
						|
        }
 | 
						|
    );
 | 
						|
 | 
						|
    add_table($out, $DB, "PaymentLogs",
 | 
						|
        cols => [
 | 
						|
            paylogs_id          => { type => 'INT', not_null => 1, unsigned => 1 },
 | 
						|
            paylogs_payments_id => { type => 'CHAR', not_null => 1, size => 16 },
 | 
						|
            paylogs_type        => { type => 'INT', not_null => 1, default => 0, unsigned => 1 }, # 0 = info, 1 = accepted, 2 = declined, 3 = error
 | 
						|
            paylogs_time        => { type => 'INT', not_null => 1, unsigned => 1 },
 | 
						|
            paylogs_viewed      => { type => 'TINYINT', not_null => 1, default => 0, unsigned => 1 },
 | 
						|
            paylogs_text        => { type => 'TEXT' },
 | 
						|
        ],
 | 
						|
        pk => 'paylogs_id',
 | 
						|
        ai => 'paylogs_id',
 | 
						|
        fk => { Payments => { paylogs_payments_id => 'payments_id' } },
 | 
						|
        index => {
 | 
						|
            pl_yt => ['paylogs_type', 'paylogs_time'],
 | 
						|
            pl_t => ['paylogs_time']
 | 
						|
        }
 | 
						|
    );
 | 
						|
 | 
						|
    recreate_table($out => $DB => ClickTrack => sub { my $table = shift; ($table->pk and @{$table->pk} != 0) },
 | 
						|
        cols => [
 | 
						|
            LinkID    => { type => 'INT', not_null => 1 },
 | 
						|
            IP        => { type => 'CHAR', size => 25, not_null => 1 },
 | 
						|
            ClickType => { type => 'ENUM', values => ['Rate', 'Hits','Review'], not_null => 1 },
 | 
						|
            ReviewID  => { type => 'INT', not_null => 1, default => 0},
 | 
						|
            Created   => { type => 'TIMESTAMP' }
 | 
						|
        ],
 | 
						|
        unique => {
 | 
						|
            ct_licr => ['LinkID', 'IP', 'ClickType','ReviewID']
 | 
						|
        },
 | 
						|
        index => {
 | 
						|
            cndx => ['Created']
 | 
						|
        }
 | 
						|
    );
 | 
						|
 | 
						|
    if (-e(my $oldconfig = "$cfg->{admin_root_path}/Links/ConfigData.pm")) {
 | 
						|
        $out->("Removing old Links/ConfigData.pm file (has been replaced with Links/Config/Data.pm)...\n");
 | 
						|
        require GT::File::Tools;
 | 
						|
        my $ret = GT::File::Tools::move($oldconfig, "$oldconfig.old");
 | 
						|
        $out->($ret ? "\tOkay!\n" : "\tAn error occured: $!\n");
 | 
						|
    }
 | 
						|
 | 
						|
    $out->(DONE '2.1.2' => '2.2.0');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__2_1_1__2_1_2 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 2.1.1 to 2.1.2
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    $out->(PERFORM '2.1.1' => '2.1.2');
 | 
						|
 | 
						|
# Add session table.
 | 
						|
    import lib $cfg->{admin_root_path};
 | 
						|
    require GT::SQL;
 | 
						|
    my $DB = GT::SQL->new( $cfg->{admin_root_path} . '/defs' );
 | 
						|
    add_table($out, $DB, 'Sessions',
 | 
						|
        cols => [
 | 
						|
            session_id      =>  { type => 'CHAR', size => 32, not_null => 1, binary => '1' },
 | 
						|
            session_user_id =>  { type => 'CHAR', not_null => 1 },
 | 
						|
            session_date    =>  { type => 'INT',  not_null => 1 },
 | 
						|
            session_data    =>  { type => 'TEXT' }
 | 
						|
        ],
 | 
						|
        pk => 'session_id',
 | 
						|
        fk => { Users => { session_user_id => 'Username' } }
 | 
						|
    );
 | 
						|
 | 
						|
    $out->(DONE '2.1.1' => '2.1.2');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__2_0_5__2_1_0 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 2.0.5 to 2.1.0
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
    $out->(PERFORM '2.0.5' => '2.1.0');
 | 
						|
 | 
						|
# Add the review table.
 | 
						|
    import lib $cfg->{admin_root_path};
 | 
						|
    require GT::SQL;
 | 
						|
    require GT::Config;
 | 
						|
    my $lang  = GT::Config->load( $cfg->{admin_root_path} . '/templates/admin/language.txt' );
 | 
						|
    my $DB = GT::SQL->new($cfg->{admin_root_path} . '/defs');
 | 
						|
 | 
						|
    add_table($out, $DB, 'Reviews',
 | 
						|
        cols => [
 | 
						|
            ReviewID            =>  { type => 'INT', not_null => 1, unsigned => 1, form_display => $lang->{'prompt_ReviewID'} },
 | 
						|
            Review_LinkID       =>  { type => 'INT', not_null => 1, unsigned => 1, regex => '^\d+$', form_display => $lang->{'prompt_Review_LinkID'} },
 | 
						|
            Review_Owner        =>  { type => 'CHAR', size => 50, not_null => 1, form_display => $lang->{'prompt_Review_Owner'} },
 | 
						|
            Review_Rating       =>  { type => 'SMALLINT', unsigned => 1, not_null => 1, default => 0, regex => '^\d+$', form_display => $lang->{'prompt_Review_Rating'} },
 | 
						|
            Review_Date         =>  { type => 'DATE', not_null => 1, form_display => $lang->{'prompt_Review_Date'} },
 | 
						|
            Review_Subject      =>  { type => 'CHAR', size => 100, not_null => 1, form_display => $lang->{'prompt_Review_Subject'} },
 | 
						|
            Review_Contents     =>  { type => 'TEXT', not_null => 1, form_display => $lang->{'prompt_Review_Contents'} },
 | 
						|
            Review_ByLine       =>  { type => 'CHAR', size => 50, form_display => $lang->{'prompt_Review_ByLine'} },
 | 
						|
            Review_WasHelpful   =>  { type => 'INT', unsigned => 1, regex => '^\d+$', form_display => $lang->{'prompt_Review_WasHelpful'} },
 | 
						|
            Review_WasNotHelpful=>  { type => 'INT', unsigned => 1, regex => '^\d+$', form_display => $lang->{'prompt_Review_WasNotHelpful'} },
 | 
						|
            Review_Validated    =>  { type => 'ENUM', values => ['No', 'Yes'], not_null => 1, default => 'No', form_display => $lang->{'prompt_Review_Validated'} },
 | 
						|
            Review_GuestName    =>  { type => 'CHAR', size => 75, form_display => $lang->{'prompt_Review_GuestName'} },
 | 
						|
            Review_GuestEmail   =>  { type => 'CHAR', size => 75, regex => '^(?:(?:.+\@.+\..+)|\s*)$', form_display => $lang->{'prompt_Review_GuestEmail'} },
 | 
						|
        ],
 | 
						|
        pk => 'ReviewID',
 | 
						|
        ai => 'ReviewID',
 | 
						|
        index => { rownerndx => ['Review_Owner'], rdatendx => ['Review_Date'], rlinkndx => ['Review_LinkID'] },
 | 
						|
        fk => { Links => { Review_LinkID => 'ID' }, Users => { Review_Owner => 'Username' }}
 | 
						|
    );
 | 
						|
 | 
						|
    add_column($out, $DB, ClickTrack => ReviewID => { type => 'INT', not_null => 1, default => 0 });
 | 
						|
 | 
						|
# Set default review options.
 | 
						|
    my %default_review = (
 | 
						|
        user_review_required   => 1,
 | 
						|
        reviews_per_page       => 5,
 | 
						|
        review_sort_by         => 'Review_Date',
 | 
						|
        review_convert_br_tags => 1,
 | 
						|
        review_days_old        => 7
 | 
						|
    );
 | 
						|
    while (my ($k, $v) = each %default_review) {
 | 
						|
        $cfg->{$k} = $v unless exists $cfg->{$k};
 | 
						|
    }
 | 
						|
 | 
						|
    $out->(DONE '2.0.5' => '2.1.0');
 | 
						|
}
 | 
						|
 | 
						|
sub upgrade__2_0_3__2_0_4 {
 | 
						|
# ---------------------------------------------------------------
 | 
						|
# Upgrade from 2.0.3 to 2.0.4.
 | 
						|
#
 | 
						|
    my ($out, $cfg) = @_;
 | 
						|
 | 
						|
    $out->(PERFORM '2.0.3' => '2.0.4');
 | 
						|
 | 
						|
    import lib $cfg->{admin_root_path};
 | 
						|
    require GT::SQL;
 | 
						|
    my $db = GT::SQL->new($cfg->{admin_root_path} . '/defs');
 | 
						|
 | 
						|
    add_column($out, $db, Links => Contact_Name => { type => 'CHAR', size => 255 });
 | 
						|
    add_column($out, $db, Links => Contact_Email => { type => 'CHAR', size => 255 });
 | 
						|
    add_column($out, $db, Category => Category_Template => { type => 'CHAR', size => 40 });
 | 
						|
    add_column($out, $db, MailingIndex => messageformat => { type => 'ENUM', values => [qw[text html]], not_null => 1, default => 'text' });
 | 
						|
 | 
						|
    $out->(DONE '2.0.3' => '2.0.4');
 | 
						|
}
 | 
						|
 | 
						|
1;
 |