File Coverage

blib/lib/MojoMojo/Schema/Result/Page.pm
Criterion Covered Total %
statement 68 81 83.9
branch 18 22 81.8
condition 1 2 50.0
subroutine 14 16 87.5
pod 11 11 100.0
total 112 132 84.8


line stmt bran cond sub pod time code
1             package MojoMojo::Schema::Result::Page;
2              
3 40     40   27230 use strict;
  40         108  
  40         1103  
4 40     40   197 use warnings;
  40         86  
  40         1019  
5 40     40   206 use Carp qw/croak/;
  40         92  
  40         2128  
6              
7 40     40   229 use parent qw/MojoMojo::Schema::Base::Result/;
  40         96  
  40         279  
8              
9             __PACKAGE__->load_components("Core");
10             __PACKAGE__->table("page");
11             __PACKAGE__->add_columns(
12             "id",
13             {
14             data_type => "INTEGER",
15             is_nullable => 0,
16             size => undef,
17             is_auto_increment => 1
18             },
19             "version",
20             { data_type => "INTEGER", is_nullable => 1, size => undef },
21             "parent",
22             { data_type => "INTEGER", is_nullable => 1, size => undef },
23             "name",
24             { data_type => "VARCHAR", is_nullable => 1, size => 200 },
25             "name_orig",
26             { data_type => "VARCHAR", is_nullable => 1, size => 200 },
27             "depth",
28             { data_type => "INTEGER", is_nullable => 1, size => undef },
29             "lft",
30             { data_type => "INTEGER", is_nullable => 1, size => undef },
31             "rgt",
32             { data_type => "INTEGER", is_nullable => 1, size => undef },
33             "content_version",
34             { data_type => "INTEGER", is_nullable => 1, size => undef },
35             );
36             __PACKAGE__->set_primary_key("id");
37             __PACKAGE__->add_unique_constraint( "page_unique_child_index",
38             [ "parent", "name" ] );
39             __PACKAGE__->has_many(
40             "wantedpages",
41             "MojoMojo::Schema::Result::WantedPage",
42             { "foreign.from_page" => "self.id" }
43             );
44             __PACKAGE__->belongs_to(
45             "parent",
46             "MojoMojo::Schema::Result::Page",
47             { id => "parent" }
48             );
49             __PACKAGE__->has_many(
50             "children",
51             "MojoMojo::Schema::Result::Page",
52             { "foreign.parent" => "self.id" }
53             );
54             __PACKAGE__->belongs_to(
55             "content",
56             "MojoMojo::Schema::Result::Content",
57             { page => "id", version => "content_version" }
58             );
59             __PACKAGE__->has_many(
60             "versions",
61             "MojoMojo::Schema::Result::Content",
62             { "foreign.page" => "self.id" },
63             { order_by => 'version desc' }
64             );
65             __PACKAGE__->belongs_to(
66             "page_version",
67             "MojoMojo::Schema::Result::PageVersion",
68             { page => "id", version => "version" }
69             );
70             __PACKAGE__->has_many(
71             "tags",
72             "MojoMojo::Schema::Result::Tag",
73             { "foreign.page" => "self.id" }
74             );
75             __PACKAGE__->has_many(
76             "links_from",
77             "MojoMojo::Schema::Result::Link",
78             { "foreign.from_page" => "self.id" }
79             );
80             __PACKAGE__->has_many(
81             "links_to",
82             "MojoMojo::Schema::Result::Link",
83             { "foreign.to_page" => "self.id" }
84             );
85             __PACKAGE__->has_many(
86             "roleprivileges",
87             "MojoMojo::Schema::Result::RolePrivilege",
88             { "foreign.page" => "self.id" }
89             );
90             __PACKAGE__->has_many(
91             "attachments",
92             "MojoMojo::Schema::Result::Attachment",
93             { "foreign.page" => "self.id" },
94             { order_by => 'name asc' }
95             );
96             __PACKAGE__->has_many(
97             "comments",
98             "MojoMojo::Schema::Result::Comment",
99             { "foreign.page" => "self.id" }
100             );
101             __PACKAGE__->has_many(
102             "journals",
103             "MojoMojo::Schema::Result::Journal",
104             { "foreign.pageid" => "self.id" }
105             );
106              
107             =head1 NAME
108              
109             MojoMojo::Schema::Result::Page - store pages
110              
111             =head1 METHODS
112              
113             =cut
114              
115             =head2 update_content <%args>
116              
117             Create a new content version for this page.
118              
119             %args is each column of L<MojoMojo::Schema::Result::Content>.
120              
121             =cut
122              
123             # update_content: this whole method may need work to deal with workflow.
124             # maybe it can't even be called if the site uses workflow...
125             # may need fixing for better conflict handling, too. maybe use a transaction?
126              
127             sub update_content {
128 6     6 1 42 my ( $self, %args ) = @_;
129              
130 6 100       118 my $content_version = (
131             $self->content
132             ? $self->content->max_version()
133             : undef
134             );
135             my %content_data =
136 6         4865 map { $_ => $args{$_} }
  72         1289  
137             $self->result_source->related_source('content')->columns;
138 6         87 my $now = DateTime->now;
139 6 100       2536 @content_data{qw/page version status release_date/} = (
140             $self->id, ( $content_version ? $content_version + 1 : 1 ),
141             'released', $now,
142             );
143 6         135 my $content =
144             $self->result_source->related_source('content')
145             ->resultset->create( \%content_data );
146 6         77896 $self->content_version( $content->version );
147 6         2394 $self->update;
148              
149 6 50       44028 $self->page_version->content_version_first($content_version)
150             unless defined $self->page_version->content_version_first;
151 6         38367 $self->page_version->content_version_last($content_version);
152 6         559 $self->page_version->update;
153              
154 6 100       8334 if ( my $previous_content = $content->previous ) {
155 1         11734 $previous_content->remove_date($now);
156 1         256 $previous_content->status('removed');
157 1         302 $previous_content->comments("Replaced by version $content_version.");
158 1         109 $previous_content->update;
159             }
160             else {
161 5         57890 $self->result_source->resultset->set_paths($self);
162             }
163 6         7777 foreach my $want_me ( $self->result_source->schema->resultset('WantedPage')
164             ->search( { to_path => $self->path } ) )
165             {
166 1         2625 my $wantme_page = $want_me->from_page;
167              
168             # convert the wanted into links
169 1         9620 $self->result_source->schema->resultset('Link')->create(
170             {
171             from_page => $wantme_page,
172             to_page => $self,
173             }
174             );
175              
176             # clear the precompiled (will be recompiled on view)
177 1 50       13104 if ( my $wantme_content = $wantme_page->content ) {
178 1         10966 $wantme_content->precompiled(undef);
179 1         288 $wantme_content->update;
180             }
181              
182             # ok, she don't want me anymore ;)
183 1         10179 $want_me->delete();
184             }
185              
186             } # end sub update_content
187              
188             =head2 add_version
189              
190             my $page_version_new = $page->add_version(
191             creator => $user_id,
192             name_orig => $page_new_name,
193             );
194              
195             Arguments: %replacementdata
196              
197             Returns: The new L<PageVersion|MojoMojo::Schema::Result::PageVersion>
198             object.
199            
200             Creates a new page version by cloning the latest version (hence pointing
201             to the same content), and replacing its values with data in the replacement
202             hash.
203              
204             Used for renaming pages.
205              
206             =cut
207              
208             sub add_version {
209 0     0 1 0 my ( $self, %args ) = @_;
210 0         0 my $now = DateTime->now;
211              
212 0         0 my $page_version_last = $self->page_version->latest_version();
213              
214             # clone the last version and update fields passed in %args
215             my %page_version_data = map {
216 0         0 exists $args{$_}
217 0 0       0 ? ( $_ => $args{$_} )
218             : ( $_ => $page_version_last->$_ )
219             } $self->result_source->related_source('page_version')->columns;
220              
221 0         0 delete $args{creator}; # creator is a field in page_version, not in page
222              
223             # for the new version, set the version number, status, and release date
224             @page_version_data{
225 0         0 qw/
226             version status release_date/
227             }
228             = ( $page_version_last->version + 1, 'released', $now );
229              
230 0         0 my $page_version_new;
231              
232             # commit the new version to the database and update the previously last version to indicate its removal
233             $self->result_source->schema->txn_do(
234             sub {
235              
236 0     0   0 $page_version_new =
237             $self->result_source->related_source('page_version')
238             ->resultset->create( \%page_version_data );
239              
240             $page_version_last->update(
241             {
242             remove_date => $now,
243             status => 'removed',
244             comments => 'Replaced by version '
245             . $page_version_data{version}
246             }
247 0         0 );
248              
249 0         0 $self->update( \%args );
250             }
251 0         0 );
252              
253 0         0 return $page_version_new;
254             }
255              
256             =head2 tagged_descendants($tag)
257              
258             Return descendants with the given tag, ordered by name.
259              
260             =cut
261              
262             sub tagged_descendants {
263 2     2 1 6068 my ( $self, $tag ) = @_;
264 2         9 my (@pages) = $self->result_source->resultset->search(
265             {
266             'ancestor.id' => $self->id,
267             'tag' => $tag,
268             -or => [
269             'me.id' => \'=ancestor.id',
270             -and =>
271             [ 'me.lft', \'> ancestor.lft', 'me.rgt', \'< ancestor.rgt', ],
272             ],
273              
274             'me.id' => \'=tag.page',
275             'content.page' => \'=me.id',
276             'content.version' => \'=me.content_version',
277             },
278             {
279             distinct => 1,
280             from => "page as me, page as ancestor, tag, content",
281             order_by => 'me.name',
282             }
283             )->all;
284 2         8701 return $self->result_source->resultset->set_paths(@pages);
285             }
286              
287             =head2 tagged_descendants_by_date
288              
289             Return descendants with the given tag, ordered by creation time, most
290             recent first.
291              
292             =cut
293              
294             sub tagged_descendants_by_date {
295 1     1 1 696 my ( $self, $tag ) = @_;
296 1         6 my (@pages) = $self->result_source->resultset->search(
297             {
298             'ancestor.id' => $self->id,
299             'tag' => $tag,
300             -or => [
301             'me.id' => \'=ancestor.id',
302             -and =>
303             [ 'me.lft', \'> ancestor.lft', 'me.rgt', \'< ancestor.rgt', ],
304             ],
305             'me.id' => \'=tag.page',
306             'content.page' => \'=me.id',
307             'content.version' => \'=me.content_version',
308             },
309             {
310             columns => [
311             'me.id', 'me.version',
312             'me.parent', 'me.name',
313             'me.name_orig', 'me.depth',
314             'me.lft', 'me.rgt',
315             'me.content_version', 'content.created'
316             ],
317             distinct => 1,
318             from => "page as me, page as ancestor, tag, content",
319             order_by => 'content.created DESC',
320             }
321             );
322 1         9718 return $self->result_source->resultset->set_paths(@pages);
323             }
324              
325             =head2 descendants
326              
327             @descendants = $page->descendants( [$resultset_page] );
328              
329             In list context, returns all descendants of this page (no paging), including
330             the page itself. In scalar context, returns the resultset object.
331              
332             If the optional $resultset_page is passed, returns that page from the
333             L<resultset|DBIx::Class::ResultSet>.
334              
335             =cut
336              
337             sub descendants {
338 7     7 1 2634 my ( $self, $resultset_page ) = @_;
339              
340 7 100 50     44 my $rs = $self->result_source->resultset->search(
341             {
342             'ancestor.id' => $self->id,
343             -or => [
344             'ancestor.id' => \'=me.id',
345             -and => [
346             'me.lft' => \'> ancestor.lft',
347             'me.rgt' => \'< ancestor.rgt',
348             ]
349             ],
350             },
351             {
352             $resultset_page ? ( page => $resultset_page || 1, rows => 20 ) : (),
353             from => 'page me, page ancestor',
354             order_by => ['me.name']
355             }
356             )
357             ; # an empty arrayref if there are no results because we'll dereference in the 'return'
358              
359             return wantarray
360 7 100       4596 ? $self->result_source->resultset->set_paths( $rs->all )
361             : $rs;
362             }
363              
364             =head2 descendants_by_date
365              
366             @descendants = $page->descendants_by_date;
367              
368             Like L</descendants>, but returns pages sorted by the dates of their
369             last content release dates and pages results (20 per page).
370              
371             =cut
372              
373             sub descendants_by_date {
374 5     5 1 15 my $self = shift;
375 5         38 my @pages = $self->result_source->resultset->search(
376             {
377             'ancestor.id' => $self->id,
378             'content.page' => \'= me.id',
379             'content.version' => \'= me.content_version',
380             -or => [
381             -and => [
382             'me.lft' => \'> ancestor.lft',
383             'me.rgt' => \'< ancestor.rgt'
384             ],
385             'ancestor.id' => \'= me.id',
386             ]
387             },
388             {
389             rows => 20,
390             page => 1,
391             from => 'page as me, page as ancestor, content',
392             order_by => 'content.created DESC'
393             }
394             );
395 5         20651 return $self->result_source->resultset->set_paths(@pages);
396             }
397              
398             =head2 user_tags($user)
399              
400             Return popular tags for this page used C<$user>.
401              
402             =cut
403              
404             sub user_tags {
405 13     13 1 4447 my ( $self, $user ) = @_;
406 13         71 my (@tags) =
407             $self->result_source->related_source('tags')->resultset->search(
408             {
409             page => $self->id,
410             person => $user,
411             },
412             {
413             select => [ 'me.tag', 'count(me.tag) as refcount' ],
414             as => [ 'tag', 'refcount' ],
415             order_by => ['refcount'],
416             group_by => ['me.tag'],
417             }
418             );
419 13         44893 return @tags;
420             }
421              
422             =head2 others_tags($user)
423              
424             Return popular tags for this page used by other people than C<$user>.
425              
426             =cut
427              
428             sub others_tags {
429 13     13 1 4399 my ( $self, $user ) = @_;
430 13         188 my (@tags) =
431             $self->result_source->related_source('tags')->resultset->search(
432             {
433             page => $self->id,
434             person => { '!=', $user }
435             },
436             {
437             select => [ 'me.tag', 'count(me.tag) as refcount' ],
438             as => [ 'tag', 'refcount' ],
439             order_by => ['refcount'],
440             group_by => ['me.tag'],
441             }
442             );
443 13         54739 return @tags;
444             }
445              
446             =head2 tags_with_counts($user)
447              
448             Return an array of {id, tag, refcount} for the C<$user>'s tags.
449              
450             =cut
451              
452             sub tags_with_counts {
453 22     22 1 83 my ( $self, $user ) = @_;
454 22         394 my (@tags) =
455             $self->result_source->related_source('tags')->resultset->search(
456             { page => $self->id, },
457             {
458             select => [ 'me.tag', 'count(me.tag) as refcount' ],
459             as => [ 'tag', 'refcount' ],
460             order_by => ['refcount'],
461             group_by => ['me.tag'],
462             }
463             );
464 22         81519 return @tags;
465             }
466              
467             =head2 path( [$path] )
468              
469             TODO Accessor?
470              
471             =cut
472              
473             sub path {
474 706     706 1 7541 my ( $self, $path ) = @_;
475 706         4820 require Carp;
476 706 100       2397 if ( defined $path ) {
477 383         1197 $self->{path} = $path;
478             }
479 706 100       2802 unless ( defined $self->{path} ) {
480 117 100       2833 return '/' if ( $self->depth == 0 );
481 10         204 $self->result_source->resultset->set_paths($self);
482              
483             # croak 'path is not set on the page object: ' . $self->name;
484             }
485 599         2804 return $self->{path};
486             }
487              
488             =head2 has_photos
489              
490             Return the number of photos attached to this page. Use for galleries.
491              
492             =cut
493              
494             sub has_photos {
495 58     58 1 181 my $self = shift;
496 58         356 return $self->result_source->schema->resultset('Photo')
497             ->search( { 'attachment.page' => $self->id },
498             { join => [qw/attachment/] } )->count;
499             }
500              
501             =head1 AUTHOR
502              
503             Marcus Ramberg <mramberg@cpan.org>
504              
505             =head1 LICENSE
506              
507             This library is free software. You can redistribute it and/or modify
508             it under the same terms as Perl itself.
509              
510             =cut
511              
512             1;