package MojoMojo::Schema::Content; use strict; use warnings; use base 'DBIx::Class'; use DateTime::Format::Mail; use Algorithm::Diff; use String::Diff; use HTML::Entities qw/encode_entities/; __PACKAGE__->load_components(qw/ResultSetManager DateTime::Epoch UTF8Columns PK::Auto Core/); __PACKAGE__->table("content"); __PACKAGE__->add_columns( "page", { data_type => "INTEGER", is_nullable => 0, size => undef }, "version", { data_type => "INTEGER", is_nullable => 0, size => undef }, "creator", { data_type => "INTEGER", is_nullable => 0, size => undef }, "created", { data_type => "BIGINT", is_nullable => 0, size => 100, epoch => 'ctime' }, "status", { data_type => "VARCHAR", is_nullable => 0, size => 20 }, "release_date", { data_type => "BIGINT", is_nullable => 0, size => 100, epoch => '1' }, "remove_date", { data_type => "BIGINT", is_nullable => 1, size => 100, epoch => '1' }, "type", { data_type => "VARCHAR", is_nullable => 1, size => 200 }, "abstract", { data_type => "TEXT", is_nullable => 1, size => 4000 }, "comments", { data_type => "TEXT", is_nullable => 1, size => 4000 }, "body", { data_type => "TEXT", is_nullable => 0, size => undef }, "precompiled", { data_type => "TEXT", is_nullable => 1, size => undef }, ); __PACKAGE__->utf8_columns(qw/body precompiled/); __PACKAGE__->set_primary_key("version", "page"); __PACKAGE__->has_many( "pages", "Page", { "foreign.content_version" => "self.version", "foreign.id" => "self.page", }, ); __PACKAGE__->has_many( "page_version_page_content_version_firsts", "PageVersion", { "foreign.content_version_first" => "self.version", "foreign.page" => "self.page", }, ); __PACKAGE__->has_many( "page_version_page_content_version_lasts", "PageVersion", { "foreign.content_version_last" => "self.version", "foreign.page" => "self.page", }, ); __PACKAGE__->belongs_to("creator", "Person", { id => "creator" }); __PACKAGE__->belongs_to("page", "Page", { id => "page" }); sub highlight { my ( $self, $c ) = @_; my $this_content = $self->formatted($c); my $previous_content = ( defined $self->previous ? $self->previous->formatted($c) : $this_content ); my $this = [ split /\n\n/, $this_content ]; my $prev = [ split /\n\n/, $previous_content ]; my @diff = Algorithm::Diff::sdiff( $prev, $this ); my $diff; my $hi = 0; for my $line (@diff) { $hi++; if ( $$line[0] eq "+" ) { $diff .= qq(
) . $$line[2] . "
"; } elsif ( $$line[0] eq "c" ) { $diff .= qq(
) . $$line[2] . "
"; } elsif ( $$line[0] eq "-" ) { } else { $diff .= $$line[1] } } return $diff; } =item formatted_diff Compare this content version to , using Algorithm::Diff. Show added lines in with diffins css class and deleted with diffdel css class. =cut sub formatted_diff { my ( $self, $c, $to, $sparse ) = @_; my $this = [ $sparse ? split /\n/, ( $self->encoded_body ) : split /\n\n/, ( $self->formatted($c) ) ]; my $prev = [ $sparse ? split /\n/, ( $to->encoded_body ) : split /\n\n/, ( $to->formatted($c) ) ]; my @diff = Algorithm::Diff::sdiff( $prev, $this ); my $diff; for my $line (@diff) { if ( $$line[0] eq "+" ) { $diff .= qq(
) . $$line[2] . "
"; } elsif ( $$line[0] eq "-" ) { $diff .= qq(
) . $$line[1] . "
"; } elsif ( $$line[0] eq "c" ) { $diff .= ($sparse ? qq(
) . $$line[1] . "
" . qq(
) . $$line[2] . "
" : String::Diff::diff_merge($$line[1], $$line[2], remove_open => '', remove_close => '', append_open => '', append_close => '', ) ); } elsif ( $$line[0] eq "u" ) { $diff .= ($sparse ? '
'.$$line[1].'
' :$$line[1] ) } else { $diff .= "Unknown operator " . $$line[0] } } return $diff; } =item formatted [] Return content after being run through MojoMojo::Formatter::* , either own content or passed =cut sub format_content : ResultSet { # FIXME: This thing should use accept-context and stop fucking around with $c everywhere my ( $self, $c, $content,$page ) = @_; $c ||= MojoMojo->instance(); MojoMojo->call_plugins( "format_content", \$content, $c, $page) if ($content); return $content; } sub formatted { my ( $self, $c) = @_; my $result=$self->result_source->resultset->format_content($c,$self->body,$self); return $result; } # create_proto: create a "proto content version" that may # be the basis for a new revision =item create_proto Create a content prototype object, as the basis for a new revision. =cut sub create_proto : ResultSet { my ( $class, $page ) = @_; my %proto_content; my @columns = __PACKAGE__->columns; eval { $page->isa('MojoMojo::Schema::Page'); $page->content->isa('MojoMojo::Schema::Content') }; if ($@) { # assume page is a simple "proto page" hashref, # or the page has no content yet %proto_content = map { $_ => undef } @columns; $proto_content{version} = 1; } else { my $content = $page->content; %proto_content = map { $_ => $content->$_ } @columns; @proto_content{qw/ creator created comments /} = (undef) x 3; $proto_content{version}++; } return \%proto_content; } =item max_version Return the highest numbered revision. =cut sub max_version { my $self=shift; my $max=$self->result_source->resultset->search({page=>$self->page->id},{ select => [ {max=>'me.version'}], as => ['max_ver'] }); return 0 unless $max->count; return $max->next->get_column('max_ver'); } =item previous Return previous version of this content, or undef for first version. =cut sub previous { my $self = shift; return $self->result_source->resultset->search({ page => $self->page->id, version => $self->version-1 })->next; } =item pub_date return publish date of this version in a format suitable for RSS 2.0 =cut sub pub_date { my $self=shift; return DateTime::Format::Mail->format_datetime($self->created); } =item store_links Extract and store all links and wanted paged from a given content version. =cut use Data::Dumper; sub store_links { my ($self) = @_; return unless ($self->status eq 'released'); my $content = $self->body; my $page = $self->page; $page->result_source->resultset->set_paths($page); $page->links_from->delete(); $page->wantedpages->delete(); require MojoMojo::Formatter::Wiki; my ($linked_pages, $wanted_pages) = MojoMojo::Formatter::Wiki->find_links( \$content, $page ); return unless (@$linked_pages || @$wanted_pages); for (@$linked_pages) { my $link = $self->result_source->schema->resultset('Link')-> find_or_create( { from_page => $self->page->id, to_page => $_->id }); } for (@$wanted_pages) { my $wanted_page = $self->result_source->schema()-> resultset('WantedPage')->find_or_create( { from_page => $page->id, to_path => $_->{path} }); } } sub encoded_body { return encode_entities(shift->body); } 1;