9 use CMS::Simple::Parse ;
10 use Template::Simple ;
14 our $VERSION = '0.01' ;
20 # use File::Path and other modules to make paths clean and portable
24 content_paths => ['content'],
25 templates_dir => 'templates',
26 output_dir => 'output',
28 # this is for timestamps
30 published_dir => 'published',
37 cont => \&CMS::Simple::Parse::parse_content,
38 csv => \&CMS::Simple::Parse::parse_csv,
40 # yaml => \&parse_yaml,
45 my( $class, $args ) = @_ ;
47 my $self = bless { %defaults, %{$args} }, $class ;
49 $self->_invert_filter_tags() ;
55 $self->_load_content() ;
58 # add template path arg
60 $self->{tmpl_obj} = Template::Simple->new( %{$self->{template_args}} ) ;
70 my $contents = $self->{contents} ;
72 #print Dumper $contents ;
74 while( my( $name, $file ) = each %{$contents} ) {
77 # ADD BETTER DIRECTORY STUFF
80 my $file_path = "$self->{content_paths}[0]/$file" ;
82 my $content_text = read_file( $file_path ) ;
84 my ($suffix) = $file_path =~ /\.(\w+)$/ ;
86 my $parser = $parsers{ $suffix } ;
88 $parser or die "unknown suffix '$suffix'" ;
90 my $parsed = $parser->( $content_text ) ;
92 $contents->{$name} = {
95 text => $content_text,
99 #print Dumper $contents ;
103 sub build_all_pages {
107 foreach my $page_name ( keys %{$self->{pages}} ) {
109 $self->build_page( $page_name ) ;
116 my( $self, $page_name ) = @_ ;
118 #print "BUILD $page_name\n" ;
120 my $page = $self->{pages}{$page_name} ;
122 return if $page->{skip} ;
124 $page->{name} = $page_name ;
126 $self->_get_page_content( $page ) ;
128 #print Dumper $page ;
130 $self->_filter_page_content( $page ) ;
133 $self->_render_page( $page ) ;
134 #print ${$page->{rendered}} ;
136 $self->_output_page( $page ) ;
139 sub _get_page_content {
141 my( $self, $page ) = @_ ;
144 # FIX so a page contents can override values and not just whole maps
145 # ADD contents maps to have multilevel keys
148 my $all_contents = $self->{contents} ;
150 my $page_contents = $page->{contents} || {} ;
152 # loop over the default (common) and page specific content maps
154 foreach my $contents_map (
155 $self->{default_contents_map},
156 $page->{contents_map} ) {
158 #print "MAP ", Dumper $contents_map ;
160 while( my( $name, $location ) = each %{$contents_map} ) {
162 # get the contents for this content name
164 my $contents = $all_contents->{$name}{parsed} ;
166 $self->_add_page_contents(
174 print Dumper $page_contents if $page->{dump} ;
176 $page->{contents} = $page_contents ;
179 sub _add_page_contents {
181 my( $self, $page_contents, $location, $contents ) = @_ ;
184 # this needs to handle multilevel content location
187 # if we have a location, just store the contents there
191 my @loc_keys = split /:/, $location ;
192 #print "LOC @loc_keys\n" ;
194 my $loc_ref = \$page_contents->{ shift @loc_keys } ;
196 #print "LOC $loc_ref\n" ;
198 # descend into the page contents based on the location keys
200 $loc_ref = \${$loc_ref}->{$_} for @loc_keys ;
202 ${$loc_ref} = deep_copy( $contents ) ;
207 # no location so store all the top level contents in the top level of
210 @{$page_contents}{keys %{$contents}} = values %{$contents} ;
213 sub _filter_page_content {
215 my( $self, $page ) = @_ ;
217 # NOTE content must be a hash at the top
219 $self->_filter_content_hash( $page->{contents} ) ;
221 #print Dumper $page->{contents} ;
224 sub _filter_content_hash {
226 my( $self, $href, $path ) = @_ ;
228 while( my( $tag, $val ) = each %{$href} ) {
231 $self->_filter_content_tag( $tag, $val, $path ) ;
233 next unless @new_val ;
234 $href->{$tag} = $new_val[0] ;
238 sub _filter_content_array {
240 my( $self, $tag, $aref, $path ) = @_ ;
242 #print "ARRAY: ", Dumper \$tag, $aref ;
246 foreach my $val ( @{$aref} ) {
249 $self->_filter_content_tag( $tag, $val, $path ) ;
252 @{$aref} = @new_vals ;
254 #print Dumper $aref ;
258 sub _filter_content_tag {
260 my( $self, $tag, $val, $path ) = @_ ;
262 my $ref_type = ref $val ;
264 if ( $ref_type eq 'HASH' ) {
266 $self->_filter_content_hash( $val, $path ) ;
270 if ( $ref_type eq 'ARRAY' ) {
272 $self->_filter_content_array( $tag, $val, $path ) ;
276 my @new_val = $self->_filter_content_value( $tag, $val, $path ) ;
278 return unless @new_val ;
282 $self->_filter_content_tag( $tag, $val, $path ) if ref $val ;
287 sub _filter_content_value {
289 my( $self, $tag, $val, $path ) = @_ ;
291 my $filters = $self->{tag_to_filters}{$tag} ;
293 return unless $filters ;
297 foreach my $filter ( @{$filters} ) {
299 #print "FILTER $filter->{name}\n" ;
301 #print "TAG $tag [$val]\n" unless defined $val;
303 $val = '' unless defined $val || $tag ne 'text' ;
305 @new_val = $filter->{code}->( $tag, $val, $path ) ;
307 next unless @new_val ;
312 #print "TAG: $tag: ", Dumper \$val ;
314 # return if nothing was changed
316 return unless @new_val ;
323 my( $self, $page ) = @_ ;
325 my $tmpl_obj = $self->{tmpl_obj} ;
327 # NOTE: using internal method. will fix template::simple to expose it
329 my $tmpl_name = $page->{template} || $self->{default_template} ;
331 my $template = $tmpl_obj->_get_template( $tmpl_name ) ;
333 #print Dumper $page->{contents} ;
335 my $rendered = $tmpl_obj->render( $template, $page->{contents} ) ;
337 $page->{rendered} = $rendered ;
344 my( $self, $page ) = @_ ;
347 # use file::path stuff to make this portable
351 "$self->{'output_dir'}/$page->{name}$self->{'output_suffix'}" ;
353 $page->{'output_path'} = $output_path ;
355 write_file( $output_path, $page->{rendered} ) ;
362 while ( my($name, $page) = each %{$self->{'pages'}} ) {
364 my $output_file = $self->{'pages'}{$name}{'output_file'} ;
366 my $remote_host = $self->{'remote_host'} ;
367 my $remote_user = $self->{'remote_user'} ;
368 my $remote_directory = $self->{'remote_directory'} ;
370 # Strip trailing slash if there is one, then replace it...
371 # so that dir always ends in slash whether or not one is passed:
372 # (Note: not portable outside Linux/unix!)
374 $remote_directory =~ s/^(.*)\/$/$1/ ;
376 my $scp = Net::SCP->new() ;
378 die "Unable to construct remote destination" unless
379 ( $remote_host && $remote_user && $remote_directory ) ;
381 # Construct remote destination from class attributes:
382 my $destination = "${remote_user}\@${remote_host}:${remote_directory}/" ;
384 # Use 'iscp' for interactive scp:
385 $scp->iscp( $output_file, $destination ) or die $scp->{errstr};
403 return $val unless ref $val ;
405 return [ map deep_copy( $_ ), @{$val} ] if ref $val eq 'ARRAY' ;
407 return { map { $_, deep_copy( $val->{$_} ) } keys %{$val} }
408 if ref $val eq 'HASH' ;
410 die "$val is not a scalar, ARRAY or HASH" ;
419 # use File::Path to make deep dirs
422 mkdir( $self->{output_dir} ) ;
423 mkdir( $self->{published_dir} ) ;
426 sub _invert_filter_tags {
432 foreach my $filter ( @{$self->{filters}} ) {
434 push @{$tag_to_filters{$_}}, $filter for @{$filter->{tags}} ;
437 #print Dumper \%tag_to_filters ;
439 $self->{tag_to_filters} = \%tag_to_filters ;