Daizu::Wc - access to database working copies


Daizu documentation Contained in the Daizu distribution.

Index


Code Index:

NAME

Top

Daizu::Wc - access to database working copies

DESCRIPTION

Top

Objects of this class provide methods for accessing the data in Daizu CMS database working copies.

METHODS

Top

Daizu::Wc->new($cms, $id)

Return an object representing a working copy in the database, given its ID number. The default if no working copy ID is given is the working copy which represents the state of the live web content (usually working copy 1).

Daizu::Wc->checkout($cms, $branch, $revnum)

Creates a new working copy on $branch (either the ID number or path of a known branch) and updates it to revision $revnum (or the latest revision if that is undef).

Returns a Daizu::Wc object for accessing it.

$wc->id

Return the ID number of the working copy.

$wc->current_revision

Return the number of the revision which the working copy is currently updated to.

$wc->file_at_path($path)

Return a Daizu::File object representing the file or directory which currently resides at $path in the working copy. Dies if there is no such file.

$wc->update($revnum)

Update a database working copy to revision $revnum. This is done in a transaction, which will be rolled back if anything goes wrong, leaving the WC in its original state. $revnum can be undef to update to the most recent revision available.

$wc->add_file($path, $data)

TODO - this method isn't safe to use yet, and will corrupt the working copy.

$wc->add_directory($path)

TODO - this method isn't safe to use yet, and will corrupt the working copy.

$wc->change_file_content($file_id, $data)

TODO - this method isn't safe to use yet, and will corrupt the working copy.

$wc->change_property($file_id, $name, $value)

TODO - this method isn't safe to use yet, and will corrupt the working copy.

$wc->commit

TODO - this doesn't do anything yet

COPYRIGHT

Top


Daizu documentation Contained in the Daizu distribution.
package Daizu::Wc;
use warnings;
use strict;

use Carp qw( croak );
use Carp::Assert qw( assert DEBUG );
use Daizu::Wc::UpdateEditor;
use Daizu::File;
use Daizu::Util qw(
    db_datetime
    db_row_exists db_row_id db_select db_insert db_update transactionally
    mint_guid guess_mime_type wc_set_file_data
    branch_id
);

sub new
{
    my ($class, $cms, $id) = @_;
    croak "bad working copy ID number '$id'"
        if defined $id && $id !~ /^\d+$/;
    $id = $cms->{live_wc_id}
        if !defined $id;

    my ($branch_id, $branch_path) = $cms->{db}->selectrow_array(q{
                select wc.branch_id, b.path
                from working_copy wc
                inner join branch b on b.id = wc.branch_id
                where wc.id = ?
        }, undef, $id);
    croak "working copy '$id' does not exist"
        unless defined $branch_id;

    return bless {
        cms => $cms,
        id => $id,
        branch_id => $branch_id,
        branch_path => $branch_path,
    }, $class;
}

sub checkout
{
    my ($class, $cms, $branch, $revnum) = @_;
    return transactionally($cms->{db}, \&_checkout_txn,
                           $class, $cms, $branch, $revnum);
}

sub _checkout_txn
{
    my ($class, $cms, $branch, $revnum) = @_;
    my $db = $cms->{db};

    my $branch_id = branch_id($db, $branch);
    my $branch_path = db_select($db, branch => $branch_id, 'path');

    my $latest_revnum = $cms->load_revision($revnum);
    $revnum = $latest_revnum unless defined $revnum;

    my $wc_id = db_insert($db, working_copy =>
        branch_id => $branch_id,
        current_revision => $revnum,
    );

    my $editor = Daizu::Wc::UpdateEditor->new(
        cms => $cms,
        db => $db,
        wc_id => $wc_id,
        revnum => $revnum,
        branch_id => $branch_id,
        branch_path => $branch_path,
    );
    my $reporter = $cms->{ra}->do_update($revnum, $branch_path, 1, $editor);
    $reporter->set_path('', 0, 1, undef);
    $reporter->finish_report;

    return $class->new($cms, $wc_id);
}

sub id { $_[0]->{id} }

sub current_revision
{
    my ($self) = @_;
    return db_select($self->{cms}{db}, working_copy => $self->{id},
                     'current_revision');
}

sub file_at_path
{
    my ($self, $path) = @_;
    my $file_id = db_row_id($self->{cms}{db}, 'wc_file',
        wc_id => $self->{id},
        path => $path,
    );
    croak "no file at '$path' in working copy $self->{id}"
        unless defined $file_id;
    return Daizu::File->new($self->{cms}, $file_id);
}

sub update
{
    my ($self, $revnum) = @_;
    return transactionally($self->{cms}{db}, \&_update_txn, $self, $revnum);
}

sub _update_txn
{
    my ($self, $revnum) = @_;
    my $cms = $self->{cms};
    my $db = $cms->{db};

    my $latest_revnum = $cms->load_revision($revnum);
    $revnum = $latest_revnum unless defined $revnum;

    my $cur_revnum = $self->current_revision;
    return $cur_revnum
        if $cur_revnum >= $revnum;

    my $editor = Daizu::Wc::UpdateEditor->new(
        cms => $self->{cms},
        db => $db,
        wc_id => $self->{id},
        revnum => $revnum,
        branch_id => $self->{branch_id},
        branch_path => $self->{branch_path},
    );
    my $reporter = $cms->{ra}->do_update($revnum, $self->{branch_path}, 1,
                                         $editor);
    $reporter->set_path('', $cur_revnum, 0, undef);
    $reporter->finish_report;

    db_update($db, working_copy => $self->{id},
        current_revision => $revnum,
    );

    $db->do(q{
                update wc_file
                set cur_revnum = ?
                where cur_revnum is not null
                    and not modified
                    and not deleted
        }, undef, $revnum);

    return $revnum;
}

sub add_file
{
    my ($self, $path, $data) = @_;
    my $wc_id = $self->{id};
    croak "you're not allowed to make changes in the live working copy"
        if $wc_id == $self->{cms}{live_wc_id};

    my $db = $self->{cms}{db};
    $db->begin_work;

    if (db_row_exists($db, 'wc_file', wc_id => $wc_id, path => $path)) {
        $db->rollback;
        croak "file or directory already exists at path '$path' in WC $wc_id";
    }

    my ($parent_id, $name);
    if ($path =~ m!^(.*)/([^/]+)!) {
        $name = $2;
        $parent_id = db_row_id($db, 'wc_file',
            path => $1,
            is_dir => 1,
        );
        croak "parent directory '$1' does not exist"
            unless defined $parent_id;
    }
    else {
        $parent_id = undef;
        $name = $path;
    }

    my $mime_type = guess_mime_type($data, $path);

    my ($guid_id) = mint_guid($self->{cms}, 0, $path, 1);   # TODO, revnum
    my $file_id = db_insert($db, 'wc_file',
        wc_id => $wc_id,
        guid_id => $guid_id,
        parent_id => $parent_id,
        is_dir => 0,
        name => $name,
        path => $path,
        modified_at => db_datetime(DateTime->now),
        content_type => $mime_type,
        data => '',
        data_len => 0,
        data_sha1 => '2jmj7l5rSw0yVb/vlWAYkK/YBwk',
    );
    wc_set_file_data($self->{cms}, $wc_id, $file_id, $mime_type, $data, 0);

    $db->commit;
    return $file_id;
}

sub add_directory
{
    my ($self, $path) = @_;
    my $wc_id = $self->{id};
    croak "you're not allowed to make changes in the live working copy"
        if $wc_id == $self->{cms}{live_wc_id};

    my $db = $self->{cms}{db};
    $db->begin_work;

    if (db_row_exists($db, 'wc_file', wc_id => $wc_id, path => $path)) {
        $db->rollback;
        croak "file or directory already exists at path '$path' in WC $wc_id";
    }

    my ($parent_id, $name);
    if ($path =~ m!^(.*)/([^/]+)!) {
        $name = $2;
        $parent_id = db_row_id($db, 'wc_file',
            path => $1,
            is_dir => 1,
        );
        croak "parent directory '$1' does not exist"
            unless defined $parent_id;
    }
    else {
        $parent_id = undef;
        $name = $path;
    }

    my ($guid_id) = mint_guid($self->{cms}, 1, $path, 1);   # TODO, revnum
    my $file_id = db_insert($db, 'wc_file',
        wc_id => $wc_id,
        guid_id => $guid_id,
        parent_id => $parent_id,
        is_dir => 1,
        name => $name,
        path => $path,
        modified_at => db_datetime(DateTime->now),
        data_len => 0,
    );

    $db->commit;
    return $file_id;
}

sub change_file_content
{
    my ($self, $file_id, $data) = @_;
    my $name = db_select($self->{cms}{db}, wc_file => $file_id, 'name');
    my $mime_type = guess_mime_type($data, $name);
    wc_set_file_data($self->{cms}, $self->{id}, $file_id, $mime_type, $data, 0);
}

sub change_property
{
    my ($self, $file_id, $name, $value) = @_;
    my $db = $self->{cms}{db};
    my $prop_id = db_row_id($db, 'wc_property',
        file_id => $file_id,
        name => $name,
    );

    if (!defined $value) {
        # Delete
        croak "can't delete non-existent property '$name' on file $file_id"
            unless defined $prop_id;
        db_update($db, wc_file => { file_id => $file_id, name => $name },
            deleted => 1,
        );
    }
    elsif (!defined $prop_id) {
        # Add
        db_insert($db, 'wc_file',
            file_id => $file_id,
            name => $name,
            value => $value,
            modified => 1,
        );
    }
    else {
        # Modify
        db_update($db, wc_file => { file_id => $file_id, name => $name },
            modified => 1,
            value => $value,
        );
    }
}

sub commit
{
    my ($self) = @_;
    my $db = $self->{cms}{db};
    my $ra = $self->{cms}{ra};

    $db->begin_work;

    my $sth = $db->prepare(q{
                select path, modified, deleted
                from wc_file
                where wc_id = ?
                order by path
        });
    $sth->execute($self->{id});
}

1;
# vi:ts=4 sw=4 expandtab