Revision 3 (by ahitrov@rambler.ru, 2010/03/24 15:19:32) The CORE
package Contenido::Project;

# ----------------------------------------------------------------------------
# Contenido::Project - �����, ������� ����� ��������� �������� ����� �
#  ����� ����������� � ������� (������, �������� �����, ����� �������� 
#  ��� title's � ��� �����). ������ ����� ������ ����� ����������� ��� 
#  ������ Apache-�������. ������ �� ����� ����� �� ������������ ���� 
#  (������� �������� ����� ��������������� ����� ������������ ���������)
#  � ������������ �� ������ ��� ��� ������� ����� �� �����.
# ----------------------------------------------------------------------------

use strict;
use warnings;
use locale;

use vars qw($VERSION $AUTOLOAD);
$VERSION = '1.0';


use Utils;
use Contenido::Globals;
use Data::Dumper;

# ----------------------------------------------------------------------------
# �����������. ������� ������ � ��������� �������.
#
# ������ ������:
#  Contenido::Project->new();
# ----------------------------------------------------------------------------
sub new
{
    my ($proto, $state, $local_keeper) = @_;
    unless ( ref $state ) {
        $log->error("������������ ����� ������������ ������� �������. � ���������� ��� ������� ������ Contenido::State.");
        die;
    }

    $local_keeper ||= $keeper;

    my $class = ref($proto) || $proto;
    my $self = {};
    bless($self, $class);
    $self->_init_;

    # �������� ��, ��� ��� ����������...
    $self->{state} = $state;
    $self->{project_name} = $state->project_name();
    $self->{debug} = $state->debug();
#   $self->{mtime} = 0;
    $self->{data} = {};

    $self->restore($local_keeper);
    return $self;
}



# ------------------------------------------------------------------------------------------------
# ������������� ���������� ��������
# ------------------------------------------------------------------------------------------------
sub _init_
{
    my $self = shift;
    $self->{attributes} = {};
    foreach my $a ( qw(project_name mtime debug) )
    {
        $self->{attributes}->{ $a } = 'SCALAR';
    }
    return 1;
}




# ------------------------------------------------------------------------------------------------
# ����� restore() ��� ���������� ������� Project �������...
#
# ������ �������������:
#  $project->restore()
# ------------------------------------------------------------------------------------------------
sub restore
{
    my $self = shift;
    my $local_keeper = shift;
    my $force = shift;
    do { $log->error("����� restore() ����� �������� ������ � ��������, �� �� �������"); die } unless ref $self;
    do { $log->error("����� restore() require established connection to DB"); die } unless (ref($local_keeper));

    return 1 if (!$force and $self->{"mtime"} and $self->{"mtime"} > time - $state->{"options_expire"});

    #TODO: implement hardcoded project attributes if db_type eq 'none'
    return 1 if $self->{state}->db_type eq 'none';

    delete $self->{"data"};
    delete $self->{"attributes"};

    my $sth = $local_keeper->SQL->prepare_cached("SELECT id, pid, name, value, type FROM options", {}, 1) || do { $log->error("Not connected to DB"); die };
    $sth->execute();

    my $data_ref = $sth->fetchall_arrayref() || [];
    $sth->finish();
    foreach (@$data_ref) {
        $_->[1] = 0 unless ($_->[1]);
    }

    $self->{data} = restore_recursive('HASH',$data_ref, 0);

    foreach my $key (keys %{$self->{"data"}}) {
        $self->{"attributes"}{$key} = "DATA";
    }

    $self->{"mtime"} = time;

    return 1;
}

sub restore_recursive {
    my $type = shift;
    my $data_ref = shift;
    my $pid = shift;

    my $struct_ref;

    if ($type eq 'ARRAY') {
        $struct_ref = [];
        foreach my $item (@$data_ref) {
            my ($id, $local_pid, $local_name, $local_value, $local_type) = @$item;
            next unless ($local_pid == $pid);
            if ($local_type eq 'SCALAR') {
                $struct_ref->[$local_name] = $local_value;
            } else {
                $struct_ref->[$local_name] = restore_recursive($local_type, $data_ref, $id);
            }
        }
    } elsif ($type eq 'HASH') {
        $struct_ref = {};
        foreach my $item (@$data_ref) {
            my ($id, $local_pid, $local_name, $local_value, $local_type) = @$item;
            next unless ($local_pid == $pid);
            if ($local_type eq 'SCALAR') {
                $struct_ref->{$local_name} = $local_value;
            } else {
                $struct_ref->{$local_name} = restore_recursive($local_type, $data_ref, $id);
            }
        }
    } elsif ($type eq 'SCALAR') {
        do { $log->error("Recursive call with scalar value... "); die };
    } else {
        do { $log->error("Wrong type in call '$type'"); die };
    }

    return $struct_ref;
}

# ----------------------------------------------------------------------------
# ����� store() ��� ���������� ������� Project �� ����... �� ���� � ��
#
# ������ �������������:
#  $project->store()
# ----------------------------------------------------------------------------

sub store
{
    my $self = shift;
    my $local_keeper = shift;

    do { $log->error("����� store() ����� �������� ������ � ��������, �� �� �������"); die }    unless ref($self);
    do { $log->error("����� store() require established connection to DB"); die } unless (ref($keeper) and $keeper->is_connected());

    return 1 if $self->{state}->db_type eq 'none';

    $local_keeper->t_connect() || do { $local_keeper->error(); return undef; };
    my $insert = $keeper->TSQL->prepare("INSERT INTO options (pid, name, value, type) VALUES (?, ?, ?, ?)");
    $local_keeper->TSQL->do("DELETE FROM options");
    store_recursive(undef, undef, $self->{"data"}, $insert, $local_keeper);
    $insert->finish();
    $local_keeper->t_finish();

    undef $self->{"mtime"};

    return 1;
}

# TODO ���������� ����� ����
sub store_recursive {
    my $pid = shift;
    my $key = shift;
    my $data = shift;
    my $insert = shift;
    my $local_keeper = shift;

    my $new_pid;

    if (ref $data eq "HASH") {
    if (defined $key) {
        $insert->execute($pid, $key, undef, "HASH");
        $new_pid = $local_keeper->TSQL->selectrow_array("SELECT currval('documents_id_seq')");
    }

    foreach my $new_key (keys %$data) {
        store_recursive($new_pid, $new_key, $data->{$new_key}, $insert, $local_keeper);
    }
    } elsif (ref $data eq "ARRAY") {
    if (defined $key) {
        $insert->execute($pid, $key, undef, "ARRAY");
        $new_pid = $local_keeper->TSQL->selectrow_array("SELECT currval('documents_id_seq')");
    }

    foreach my $new_key (0 .. $#$data) {
        store_recursive($new_pid, $new_key, $data->[$new_key], $insert, $local_keeper);
    }
    } else {
    $insert->execute($pid, $key, $data, "SCALAR") || return $local_keeper->t_abort();
    }
}

# ----------------------------------------------------------------------------
# ��� ����� AUTOLOAD. ����� ������� ��� ���������/������ �����...
# ������ 0.2/���������������� ������
# ----------------------------------------------------------------------------

sub AUTOLOAD
{
    my $self = shift;
    my $attribute = $AUTOLOAD;


    $attribute =~ s/.*:://;
    return undef    unless  $attribute =~ /[^A-Z]/;     # ��������� ������ ���� DESTROY


    if (! exists($self->{attributes}->{$attribute}))
    {
        $log->error("����� ������, ��� �������� �� ���������� ��������������� ��������: ->$attribute(). ������� ������.");
        $self->{attributes}->{$attribute} = 'DATA';
    }

    if ($self->{attributes}->{$attribute} eq 'DATA')
    {
        $self->{data}->{ $attribute } = shift @_    if (scalar(@_) > 0);
        return $self->{data}->{ $attribute };
    } else {
        $self->{ $attribute } = shift @_    if (scalar(@_) > 0);
        return $self->{ $attribute };
    }
}

1;