| 1 | # Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd. |
|---|
| 2 | # This program is distributed under the terms of the |
|---|
| 3 | # GNU General Public License, version 2. |
|---|
| 4 | # |
|---|
| 5 | # $Id$ |
|---|
| 6 | |
|---|
| 7 | package MT::Template; |
|---|
| 8 | |
|---|
| 9 | use strict; |
|---|
| 10 | use base qw( MT::Object ); |
|---|
| 11 | use MT::Util qw( weaken ); |
|---|
| 12 | |
|---|
| 13 | use MT::Template::Node; |
|---|
| 14 | sub NODE () { 'MT::Template::Node' } |
|---|
| 15 | |
|---|
| 16 | my $resync_to_db; |
|---|
| 17 | |
|---|
| 18 | __PACKAGE__->install_properties({ |
|---|
| 19 | column_defs => { |
|---|
| 20 | 'id' => 'integer not null auto_increment', |
|---|
| 21 | 'blog_id' => 'integer not null', |
|---|
| 22 | 'name' => 'string(255) not null', |
|---|
| 23 | 'type' => 'string(25) not null', |
|---|
| 24 | 'outfile' => 'string(255)', |
|---|
| 25 | 'text' => 'text', |
|---|
| 26 | 'linked_file' => 'string(255)', |
|---|
| 27 | 'linked_file_mtime' => 'string(10)', |
|---|
| 28 | 'linked_file_size' => 'integer', |
|---|
| 29 | 'rebuild_me' => 'boolean', |
|---|
| 30 | 'build_dynamic' => 'boolean', |
|---|
| 31 | 'identifier' => 'string(50)', |
|---|
| 32 | 'build_type' => 'smallint', |
|---|
| 33 | 'build_interval' => 'integer', |
|---|
| 34 | |
|---|
| 35 | # meta properties |
|---|
| 36 | 'last_rebuild_time' => 'integer meta', |
|---|
| 37 | 'page_layout' => 'string meta', |
|---|
| 38 | 'include_with_ssi' => 'integer meta', |
|---|
| 39 | 'cache_expire_type' => 'integer meta', |
|---|
| 40 | 'cache_expire_interval' => 'integer meta', |
|---|
| 41 | 'cache_expire_event' => 'string meta', |
|---|
| 42 | 'cache_path' => 'string meta', |
|---|
| 43 | 'modulesets' => 'string meta', |
|---|
| 44 | }, |
|---|
| 45 | indexes => { |
|---|
| 46 | blog_id => 1, |
|---|
| 47 | name => 1, |
|---|
| 48 | type => 1, |
|---|
| 49 | outfile => 1, |
|---|
| 50 | identifier => 1, |
|---|
| 51 | }, |
|---|
| 52 | defaults => { |
|---|
| 53 | 'rebuild_me' => 1, |
|---|
| 54 | 'build_dynamic' => 0, |
|---|
| 55 | 'build_type' => 1, |
|---|
| 56 | 'build_interval' => 0, |
|---|
| 57 | }, |
|---|
| 58 | meta => 1, |
|---|
| 59 | child_of => 'MT::Blog', |
|---|
| 60 | child_classes => [ 'MT::TemplateMap', 'MT::FileInfo' ], |
|---|
| 61 | audit => 1, |
|---|
| 62 | datasource => 'template', |
|---|
| 63 | primary_key => 'id', |
|---|
| 64 | }); |
|---|
| 65 | __PACKAGE__->add_trigger('pre_remove' => \&pre_remove_children); |
|---|
| 66 | |
|---|
| 67 | use MT::Builder; |
|---|
| 68 | use MT::Blog; |
|---|
| 69 | use File::Spec; |
|---|
| 70 | |
|---|
| 71 | sub class_label { |
|---|
| 72 | MT->translate("Template"); |
|---|
| 73 | } |
|---|
| 74 | |
|---|
| 75 | sub class_label_plural { |
|---|
| 76 | MT->translate("Templates"); |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | sub new { |
|---|
| 80 | my $pkg = shift; |
|---|
| 81 | my (%param) = @_; |
|---|
| 82 | if (my $type = delete $param{type}) { |
|---|
| 83 | if ($type eq 'filename') { |
|---|
| 84 | return $pkg->new_file($param{source}, %param); |
|---|
| 85 | } elsif ($type eq 'scalarref') { |
|---|
| 86 | return $pkg->new_string($param{source}, %param); |
|---|
| 87 | } |
|---|
| 88 | } |
|---|
| 89 | my $tmpl = $pkg->SUPER::new(@_); |
|---|
| 90 | $tmpl->{include_path} = $param{path}; |
|---|
| 91 | $tmpl->{include_filter} = $param{filter}; |
|---|
| 92 | return $tmpl; |
|---|
| 93 | } |
|---|
| 94 | |
|---|
| 95 | sub new_file { |
|---|
| 96 | my $pkg = shift; |
|---|
| 97 | my ($file, %param) = @_; |
|---|
| 98 | my $tmpl = $pkg->new; |
|---|
| 99 | $tmpl->{include_path} = $param{path}; |
|---|
| 100 | $tmpl->{include_filter} = $param{filter}; |
|---|
| 101 | $tmpl->{__file} = $file; |
|---|
| 102 | my $contents = $tmpl->load_file($file); |
|---|
| 103 | if (defined $contents) { |
|---|
| 104 | if ($tmpl->{include_filter}) { |
|---|
| 105 | $tmpl->{include_filter}->(\$contents, $file); |
|---|
| 106 | } |
|---|
| 107 | $tmpl->text($contents); |
|---|
| 108 | return $tmpl; |
|---|
| 109 | } |
|---|
| 110 | return; # load_file errror; |
|---|
| 111 | } |
|---|
| 112 | |
|---|
| 113 | sub new_string { |
|---|
| 114 | my $pkg = shift; |
|---|
| 115 | my ($str, %param) = @_; |
|---|
| 116 | my $tmpl = $pkg->new; |
|---|
| 117 | $tmpl->{include_path} = $param{path}; |
|---|
| 118 | $tmpl->{include_filter} = $param{filter}; |
|---|
| 119 | if (ref($str) && defined($$str)) { |
|---|
| 120 | if ($tmpl->{include_filter}) { |
|---|
| 121 | $tmpl->{include_filter}->($str); |
|---|
| 122 | } |
|---|
| 123 | $tmpl->text($$str); |
|---|
| 124 | } |
|---|
| 125 | return $tmpl; |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | sub load_file { |
|---|
| 129 | my $tmpl = shift; |
|---|
| 130 | my ($file) = @_; |
|---|
| 131 | unless (File::Spec->file_name_is_absolute($file)) { |
|---|
| 132 | my @paths = @{ $tmpl->{include_path} || [] }; |
|---|
| 133 | foreach my $path (@paths) { |
|---|
| 134 | my $test_file = File::Spec->catfile($path, $file); |
|---|
| 135 | $file = $test_file, last if -f $test_file; |
|---|
| 136 | } |
|---|
| 137 | } |
|---|
| 138 | return $tmpl->trans_error("File not found: [_1]", $file) unless -e $file; |
|---|
| 139 | local *FH; |
|---|
| 140 | open FH, $file |
|---|
| 141 | or return $tmpl->trans_error("Error reading file '[_1]': [_2]", $file, $!); |
|---|
| 142 | my $c; |
|---|
| 143 | do { local $/; $c = <FH> }; |
|---|
| 144 | close FH; |
|---|
| 145 | return $c; |
|---|
| 146 | } |
|---|
| 147 | |
|---|
| 148 | sub context { |
|---|
| 149 | my $tmpl = shift; |
|---|
| 150 | return $tmpl->{context} = shift if @_; |
|---|
| 151 | require MT::Template::Context; |
|---|
| 152 | my $ctx = $tmpl->{context} ||= MT::Template::Context->new; |
|---|
| 153 | weaken($ctx->{__stash}{'template'} = $tmpl); |
|---|
| 154 | return $ctx; |
|---|
| 155 | } |
|---|
| 156 | |
|---|
| 157 | sub param { |
|---|
| 158 | my $tmpl = shift; |
|---|
| 159 | my $ctx = $tmpl->context; |
|---|
| 160 | if (@_ == 1) { |
|---|
| 161 | if (ref($_[0]) eq 'HASH') { |
|---|
| 162 | $ctx->var($_, $_[0]->{$_}) for keys %{ $_[0] }; |
|---|
| 163 | } else { |
|---|
| 164 | return $ctx->var($_[0]); |
|---|
| 165 | } |
|---|
| 166 | } elsif (@_ == 2) { |
|---|
| 167 | $ctx->var($_[0], $_[1]); |
|---|
| 168 | } else { |
|---|
| 169 | return $ctx->{__stash}{vars}; |
|---|
| 170 | } |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | sub clear_params { |
|---|
| 174 | my $tmpl = shift; |
|---|
| 175 | my $ctx = $tmpl->context; |
|---|
| 176 | %{$ctx->{__stash}{vars}} = (); |
|---|
| 177 | } |
|---|
| 178 | |
|---|
| 179 | sub reflow { |
|---|
| 180 | my $tmpl = shift; |
|---|
| 181 | my ($tokens) = @_; |
|---|
| 182 | $tokens ||= $tmpl->tokens; |
|---|
| 183 | |
|---|
| 184 | # reconstitute text of template based on tokens |
|---|
| 185 | my $str = ''; |
|---|
| 186 | foreach my $token (@$tokens) { |
|---|
| 187 | my $tag = $token->tag; |
|---|
| 188 | if ($tag eq 'TEXT') { |
|---|
| 189 | $str .= $token->nodeValue; |
|---|
| 190 | } else { |
|---|
| 191 | $str .= '<mt' . $tag; |
|---|
| 192 | if (my $attrs = $token->attribute_list) { |
|---|
| 193 | my $attrh = $token->attributes; |
|---|
| 194 | foreach my $a (@$attrs) { |
|---|
| 195 | delete $attrh->{$a->[0]}; |
|---|
| 196 | my $v = $a->[1]; |
|---|
| 197 | $v = $v =~ m/"/ ? qq{'$v'} : qq{"$v"}; |
|---|
| 198 | $str .= ' ' . $a->[0] . '=' . $v; |
|---|
| 199 | } |
|---|
| 200 | foreach my $a (keys %$attrh) { |
|---|
| 201 | my $v = $attrh->{$a}; |
|---|
| 202 | $v = $v =~ m/"/ ? qq{'$v'} : qq{"$v"}; |
|---|
| 203 | $str .= ' ' . $a . '=' . $v; |
|---|
| 204 | } |
|---|
| 205 | } |
|---|
| 206 | $str .= '>'; |
|---|
| 207 | if (my $childNodes = $token->childNodes) { |
|---|
| 208 | # container tag |
|---|
| 209 | $str .= $tmpl->reflow($childNodes); |
|---|
| 210 | $str .= '</mt' . $tag . '>'; |
|---|
| 211 | } |
|---|
| 212 | } |
|---|
| 213 | } |
|---|
| 214 | return $str; |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | sub build { |
|---|
| 218 | my $tmpl = shift; |
|---|
| 219 | my($ctx, $cond) = @_; |
|---|
| 220 | $ctx ||= $tmpl->context; |
|---|
| 221 | |
|---|
| 222 | my $timer = MT->get_timer(); |
|---|
| 223 | local $timer->{elapsed} = 0 if $timer; |
|---|
| 224 | |
|---|
| 225 | local $ctx->{__stash}{template} = $tmpl; |
|---|
| 226 | my $tokens = $tmpl->tokens |
|---|
| 227 | or return; |
|---|
| 228 | my $build = $ctx->{__stash}{builder} || MT::Builder->new; |
|---|
| 229 | my $page_layout; |
|---|
| 230 | if (my $blog_id = $tmpl->blog_id) { |
|---|
| 231 | $ctx->stash('blog_id', $blog_id); |
|---|
| 232 | my $blog = $ctx->stash('blog'); |
|---|
| 233 | unless ($blog) { |
|---|
| 234 | $blog = MT::Blog->load($blog_id) or |
|---|
| 235 | return $tmpl->error(MT->translate( |
|---|
| 236 | "Load of blog '[_1]' failed: [_2]", $blog_id, MT::Blog->errstr )); |
|---|
| 237 | $ctx->stash('blog', $blog); |
|---|
| 238 | } else { |
|---|
| 239 | $ctx->stash('blog_id', $blog->id); |
|---|
| 240 | } |
|---|
| 241 | MT->config->TimeOffset($blog->server_offset); |
|---|
| 242 | $page_layout = $blog->page_layout; |
|---|
| 243 | } |
|---|
| 244 | $page_layout = $tmpl->page_layout if $tmpl->page_layout; |
|---|
| 245 | $ctx->var( 'page_layout', $page_layout ) |
|---|
| 246 | unless $ctx->var('page_layout'); |
|---|
| 247 | if (my $layout = $ctx->var('page_layout')) { |
|---|
| 248 | my $columns = { |
|---|
| 249 | 'layout-wt' => 2, |
|---|
| 250 | 'layout-tw' => 2, |
|---|
| 251 | 'layout-wm' => 2, |
|---|
| 252 | 'layout-mw' => 2, |
|---|
| 253 | 'layout-wtt' => 3, |
|---|
| 254 | 'layout-twt' => 3, |
|---|
| 255 | }->{$layout}; |
|---|
| 256 | $ctx->var( 'page_columns', $columns ) if $columns; |
|---|
| 257 | } |
|---|
| 258 | $ctx->var( $tmpl->identifier, 1 ) if defined $tmpl->identifier; |
|---|
| 259 | |
|---|
| 260 | $timer->pause_partial if $timer; |
|---|
| 261 | |
|---|
| 262 | my $res = $build->build($ctx, $tokens, $cond); |
|---|
| 263 | |
|---|
| 264 | if ($timer) { |
|---|
| 265 | $timer->mark("MT::Template::build[" . ($tmpl->name || $tmpl->{__file} || "?").']'); |
|---|
| 266 | } |
|---|
| 267 | |
|---|
| 268 | unless (defined($res)) { |
|---|
| 269 | return $tmpl->error(MT->translate( |
|---|
| 270 | "Publish error in template '[_1]': [_2]", |
|---|
| 271 | $tmpl->name || $tmpl->{__file}, $build->errstr)); |
|---|
| 272 | } |
|---|
| 273 | $res =~ s/^\s*//; |
|---|
| 274 | return $res; |
|---|
| 275 | } |
|---|
| 276 | |
|---|
| 277 | sub output { |
|---|
| 278 | my $tmpl = shift; |
|---|
| 279 | my ($param) = @_; |
|---|
| 280 | $tmpl->param($param) if $param; |
|---|
| 281 | return $tmpl->build(); |
|---|
| 282 | } |
|---|
| 283 | |
|---|
| 284 | sub widgets_to_modulesets { |
|---|
| 285 | my $pkg = shift; |
|---|
| 286 | my ( $widgets, $blog_id ) = @_; |
|---|
| 287 | return unless $widgets && @$widgets; |
|---|
| 288 | |
|---|
| 289 | my @wtmpls = $pkg->load( |
|---|
| 290 | { name => $widgets, blog_id => $blog_id ? [ $blog_id, 0 ] : 0, type => 'widget' } |
|---|
| 291 | ) if $widgets && @$widgets; |
|---|
| 292 | my @wids; |
|---|
| 293 | foreach my $name ( @$widgets ) { |
|---|
| 294 | my ( $widget ) = grep { $_->name eq $name } @wtmpls; |
|---|
| 295 | next unless $widget; |
|---|
| 296 | push @wids, $widget->id; |
|---|
| 297 | } |
|---|
| 298 | return join ',', @wids; |
|---|
| 299 | } |
|---|
| 300 | |
|---|
| 301 | sub save_widgetset { |
|---|
| 302 | my $obj = shift; |
|---|
| 303 | |
|---|
| 304 | my $ms = $obj->modulesets; |
|---|
| 305 | # build module list |
|---|
| 306 | my @inst; |
|---|
| 307 | if ( $ms && $ms =~ /;/ ) { |
|---|
| 308 | my @mods = split /;/, $ms; |
|---|
| 309 | for (@mods) { |
|---|
| 310 | # tmpl_id = column index . order in column ; |
|---|
| 311 | my ($id, $col) = /(\d+)=(\d+)\.(\d+)/; |
|---|
| 312 | push @inst, $id if $col && ( $col == 1 ); |
|---|
| 313 | } |
|---|
| 314 | $obj->modulesets( join ',', @inst ); |
|---|
| 315 | } |
|---|
| 316 | else { |
|---|
| 317 | @inst = split /,/, $obj->modulesets; |
|---|
| 318 | } |
|---|
| 319 | |
|---|
| 320 | my @widgets = MT::Template->load( |
|---|
| 321 | { id => \@inst, type => 'widget', |
|---|
| 322 | blog_id => $obj->blog_id ? [ 0, $obj->blog_id ] : '0' }, |
|---|
| 323 | { fetchonly => [ 'id', 'name' ] } |
|---|
| 324 | ) if @inst; |
|---|
| 325 | |
|---|
| 326 | my $string_tmpl = '<mt:include widget="%s">'; |
|---|
| 327 | my $text = q(); |
|---|
| 328 | my @ids; |
|---|
| 329 | foreach my $wid (@inst) { |
|---|
| 330 | my ( $tmpl ) = grep { $_->id eq $wid } @widgets; |
|---|
| 331 | next unless $tmpl; |
|---|
| 332 | $text .= sprintf( $string_tmpl, $tmpl->name ); |
|---|
| 333 | push @ids, $wid; |
|---|
| 334 | } |
|---|
| 335 | $obj->modulesets( join ',', @ids ) |
|---|
| 336 | if scalar @ids != scalar @inst; |
|---|
| 337 | $obj->text($text); |
|---|
| 338 | return $obj->SUPER::save; |
|---|
| 339 | } |
|---|
| 340 | |
|---|
| 341 | sub save { |
|---|
| 342 | my $tmpl = shift; |
|---|
| 343 | my $existing = MT::Template->load({ name => $tmpl->name, blog_id => $tmpl->blog_id }); |
|---|
| 344 | if ($existing && (!$tmpl->id || ($tmpl->id && ($existing->id ne $tmpl->id))) |
|---|
| 345 | && ($existing->type eq $tmpl->type)) { |
|---|
| 346 | return $tmpl->error(MT->translate('Template with the same name already exists in this blog.')); |
|---|
| 347 | } |
|---|
| 348 | |
|---|
| 349 | if ( 'widgetset' eq $tmpl->type ) { |
|---|
| 350 | return $tmpl->save_widgetset(); |
|---|
| 351 | } |
|---|
| 352 | |
|---|
| 353 | if ($tmpl->id && ($tmpl->is_changed('build_type'))) { |
|---|
| 354 | # check for templatemaps, and update them appropriately |
|---|
| 355 | require MT::TemplateMap; |
|---|
| 356 | require MT::PublishOption; |
|---|
| 357 | my @maps = MT::TemplateMap->load({ template_id => $tmpl->id }); |
|---|
| 358 | foreach my $map (@maps) { |
|---|
| 359 | if ( ($map->build_type || 0) != ($tmpl->build_type || 0) ) { |
|---|
| 360 | $map->build_type($tmpl->build_type); |
|---|
| 361 | $map->save or die $map->errstr; |
|---|
| 362 | } |
|---|
| 363 | } |
|---|
| 364 | } |
|---|
| 365 | |
|---|
| 366 | if ($tmpl->linked_file) { |
|---|
| 367 | $tmpl->_sync_to_disk($tmpl->SUPER::text) or return; |
|---|
| 368 | } |
|---|
| 369 | $tmpl->{needs_db_sync} = 0; |
|---|
| 370 | |
|---|
| 371 | $tmpl->SUPER::save; |
|---|
| 372 | } |
|---|
| 373 | |
|---|
| 374 | sub build_dynamic { |
|---|
| 375 | my $tmpl = shift; |
|---|
| 376 | return $tmpl->SUPER::build_dynamic($_[0]) if @_; |
|---|
| 377 | require MT::PublishOption; |
|---|
| 378 | return 1 if $tmpl->build_type == MT::PublishOption::DYNAMIC(); |
|---|
| 379 | return $tmpl->SUPER::build_dynamic; |
|---|
| 380 | } |
|---|
| 381 | |
|---|
| 382 | sub blog { |
|---|
| 383 | my $this = shift; |
|---|
| 384 | return undef unless $this->blog_id; |
|---|
| 385 | return $this->{__blog} if $this->{__blog}; |
|---|
| 386 | return $this->{__blog} = MT::Blog->load($this->blog_id); |
|---|
| 387 | } |
|---|
| 388 | |
|---|
| 389 | sub set_values_internal { |
|---|
| 390 | my $tmpl = shift; |
|---|
| 391 | my ($cols) = @_; |
|---|
| 392 | if (exists $cols->{text}) { |
|---|
| 393 | # The text column of the MT::Template object can be associated |
|---|
| 394 | # with a physical file. When loading the record data from the |
|---|
| 395 | # database, we should observe whether or not it is in sync with |
|---|
| 396 | # the physical file. This logic handles the case where the |
|---|
| 397 | # record is loaded through the MT::ObjectDriver and should |
|---|
| 398 | # not apply for any other use of $tmpl->set_values, since those |
|---|
| 399 | # should all be explicitly setting the template text. |
|---|
| 400 | my @info = caller(); |
|---|
| 401 | if ($info[0] =~ m/^MT::ObjectDriver::/) { |
|---|
| 402 | if ($cols->{linked_file}) { |
|---|
| 403 | my %local_cols = %$cols; |
|---|
| 404 | delete $local_cols{text}; |
|---|
| 405 | $tmpl->SUPER::set_values_internal(\%local_cols); |
|---|
| 406 | my $sync_text = $tmpl->text(); |
|---|
| 407 | if (!defined $sync_text) { |
|---|
| 408 | $tmpl->text($cols->{text}); |
|---|
| 409 | } |
|---|
| 410 | return; |
|---|
| 411 | } |
|---|
| 412 | } |
|---|
| 413 | } |
|---|
| 414 | $tmpl->SUPER::set_values_internal(@_); |
|---|
| 415 | } |
|---|
| 416 | |
|---|
| 417 | sub text { |
|---|
| 418 | my $tmpl = shift; |
|---|
| 419 | my $text; |
|---|
| 420 | if ($tmpl->{reflow_flag}) { |
|---|
| 421 | $tmpl->{reflow_flag} = 0; |
|---|
| 422 | $text = $tmpl->reflow(); |
|---|
| 423 | } |
|---|
| 424 | $text = $tmpl->SUPER::text(@_); |
|---|
| 425 | |
|---|
| 426 | $tmpl->{needs_db_sync} = 0; |
|---|
| 427 | unless (@_) { |
|---|
| 428 | if ($tmpl->linked_file) { |
|---|
| 429 | if (my $res = $tmpl->_sync_from_disk) { |
|---|
| 430 | $text = $res; |
|---|
| 431 | $tmpl->SUPER::text($text); |
|---|
| 432 | ## We used to save the template here; now we don't, because |
|---|
| 433 | ## it causes deadlock (the DB is locked from loading the |
|---|
| 434 | ## template, so saving would try to write-lock it). |
|---|
| 435 | if (!defined $resync_to_db) { |
|---|
| 436 | $resync_to_db = {}; |
|---|
| 437 | MT->add_callback('takedown', 9, undef, \&_resync_to_db); |
|---|
| 438 | } |
|---|
| 439 | $resync_to_db->{$tmpl->id} = $tmpl; |
|---|
| 440 | $tmpl->{needs_db_sync} = 1; |
|---|
| 441 | } |
|---|
| 442 | } |
|---|
| 443 | $tmpl->reset_tokens; |
|---|
| 444 | } |
|---|
| 445 | $text; |
|---|
| 446 | } |
|---|
| 447 | |
|---|
| 448 | sub _resync_to_db { |
|---|
| 449 | return unless defined $resync_to_db; |
|---|
| 450 | return unless %$resync_to_db; |
|---|
| 451 | foreach my $tmpl_id (keys %$resync_to_db) { |
|---|
| 452 | my $tmpl = $resync_to_db->{$tmpl_id}; |
|---|
| 453 | next unless $tmpl->{needs_db_sync}; |
|---|
| 454 | $tmpl->save; |
|---|
| 455 | } |
|---|
| 456 | $resync_to_db = {}; |
|---|
| 457 | } |
|---|
| 458 | |
|---|
| 459 | sub _sync_from_disk { |
|---|
| 460 | my $tmpl = shift; |
|---|
| 461 | my $lfile = $tmpl->linked_file; |
|---|
| 462 | unless (File::Spec->file_name_is_absolute($lfile)) { |
|---|
| 463 | if ($tmpl->blog_id) { |
|---|
| 464 | my $blog = MT::Blog->load($tmpl->blog_id) |
|---|
| 465 | or return; |
|---|
| 466 | $lfile = File::Spec->catfile($blog->site_path, $lfile); |
|---|
| 467 | } |
|---|
| 468 | else { |
|---|
| 469 | # use MT path to base relative paths |
|---|
| 470 | $lfile = File::Spec->catfile(MT->instance->server_path, $lfile); |
|---|
| 471 | } |
|---|
| 472 | } |
|---|
| 473 | return unless -e $lfile; |
|---|
| 474 | my($size, $mtime) = (stat _)[7,9]; |
|---|
| 475 | return if $size == $tmpl->linked_file_size && |
|---|
| 476 | $mtime == $tmpl->linked_file_mtime; |
|---|
| 477 | local *FH; |
|---|
| 478 | open FH, $lfile or return; |
|---|
| 479 | my $c; do { local $/; $c = <FH> }; |
|---|
| 480 | close FH; |
|---|
| 481 | $tmpl->linked_file_size($size); |
|---|
| 482 | $tmpl->linked_file_mtime($mtime); |
|---|
| 483 | $c; |
|---|
| 484 | } |
|---|
| 485 | |
|---|
| 486 | sub _sync_to_disk { |
|---|
| 487 | my $tmpl = shift; |
|---|
| 488 | my($text) = @_; |
|---|
| 489 | my $lfile = $tmpl->linked_file; |
|---|
| 490 | my $cfg = MT->config; |
|---|
| 491 | if ($cfg->SafeMode) { |
|---|
| 492 | ## Check for a set of extensions that aren't allowed. |
|---|
| 493 | for my $ext (qw( pl pm cgi cfg )) { |
|---|
| 494 | if ($lfile =~ /\.$ext$/i) { |
|---|
| 495 | return $tmpl->error(MT->translate( |
|---|
| 496 | "You cannot use a [_1] extension for a linked file.", |
|---|
| 497 | ".$ext")); |
|---|
| 498 | } |
|---|
| 499 | } |
|---|
| 500 | } |
|---|
| 501 | unless (File::Spec->file_name_is_absolute($lfile)) { |
|---|
| 502 | if ($tmpl->blog_id) { |
|---|
| 503 | my $blog = MT::Blog->load($tmpl->blog_id) |
|---|
| 504 | or return; |
|---|
| 505 | $lfile = File::Spec->catfile($blog->site_path, $lfile); |
|---|
| 506 | } else { |
|---|
| 507 | $lfile = File::Spec->catfile(MT->instance->server_path, $lfile); |
|---|
| 508 | } |
|---|
| 509 | } |
|---|
| 510 | local *FH; |
|---|
| 511 | ## If the linked file already exists, and there is no template text |
|---|
| 512 | ## (empty textarea, etc.), then we read the template text from the |
|---|
| 513 | ## linked file, assuming that it should not be overwritten. If the |
|---|
| 514 | ## file does not already exist, or if there is template text, assume |
|---|
| 515 | ## that we should update the linked file. |
|---|
| 516 | if (-e $lfile && !$tmpl->SUPER::text) { |
|---|
| 517 | open FH, $lfile or return; |
|---|
| 518 | do { local $/; $tmpl->SUPER::text(<FH>) }; |
|---|
| 519 | close FH; |
|---|
| 520 | } else { |
|---|
| 521 | my $umask = oct $cfg->HTMLUmask; |
|---|
| 522 | my $old = umask($umask); |
|---|
| 523 | ## Untaint. We assume that the user knows what he/she is doing, |
|---|
| 524 | ## and allow anything. |
|---|
| 525 | ($lfile) = $lfile =~ /(.+)/s; |
|---|
| 526 | open FH, ">$lfile" or |
|---|
| 527 | return $tmpl->error(MT->translate( |
|---|
| 528 | "Opening linked file '[_1]' failed: [_2]", $lfile, "$!" )); |
|---|
| 529 | print FH $text; |
|---|
| 530 | close FH; |
|---|
| 531 | umask($old); |
|---|
| 532 | } |
|---|
| 533 | my($size, $mtime) = (stat $lfile)[7,9]; |
|---|
| 534 | $tmpl->linked_file_size($size); |
|---|
| 535 | $tmpl->linked_file_mtime($mtime); |
|---|
| 536 | 1; |
|---|
| 537 | } |
|---|
| 538 | |
|---|
| 539 | sub rescan { |
|---|
| 540 | my $tmpl = shift; |
|---|
| 541 | my ($tokens) = @_; |
|---|
| 542 | unless ($tokens) { |
|---|
| 543 | # top of tree; reset |
|---|
| 544 | $tmpl->{__ids} = {}; |
|---|
| 545 | $tmpl->{__classes} = {}; |
|---|
| 546 | # Use tokens if we already have them, otherwise compile |
|---|
| 547 | $tokens = $tmpl->{__tokens} || $tmpl->compile; |
|---|
| 548 | } |
|---|
| 549 | return unless $tokens; |
|---|
| 550 | foreach my $t (@$tokens) { |
|---|
| 551 | if ($t->tag ne 'TEXT') { |
|---|
| 552 | if (my $id = $t->getAttribute('id')) { |
|---|
| 553 | my $ids = $tmpl->{__ids} ||= {}; |
|---|
| 554 | $ids->{lc $id} = $t; |
|---|
| 555 | } |
|---|
| 556 | elsif (my $class = $t->getAttribute('class')) { |
|---|
| 557 | my $classes = $tmpl->{__classes} ||= {}; |
|---|
| 558 | push @{ $classes->{lc $class} ||= [] }, $t; |
|---|
| 559 | } |
|---|
| 560 | if (my $childNodes = $t->childNodes) { |
|---|
| 561 | $tmpl->rescan($childNodes); |
|---|
| 562 | } |
|---|
| 563 | } |
|---|
| 564 | } |
|---|
| 565 | } |
|---|
| 566 | |
|---|
| 567 | sub compile { |
|---|
| 568 | my $tmpl = shift; |
|---|
| 569 | require MT::Builder; |
|---|
| 570 | my $b = new MT::Builder; |
|---|
| 571 | $b->compile($tmpl) or return $tmpl->error($b->errstr); |
|---|
| 572 | return $tmpl->{__tokens}; |
|---|
| 573 | } |
|---|
| 574 | |
|---|
| 575 | sub errors { |
|---|
| 576 | my $tmpl = shift; |
|---|
| 577 | $tmpl->{errors} = shift if @_; |
|---|
| 578 | $tmpl->{errors}; |
|---|
| 579 | } |
|---|
| 580 | |
|---|
| 581 | sub reset_tokens { |
|---|
| 582 | my $tmpl = shift; |
|---|
| 583 | $tmpl->{__tokens} = undef; |
|---|
| 584 | $tmpl->{__classes} = undef; |
|---|
| 585 | $tmpl->{__ids} = undef; |
|---|
| 586 | } |
|---|
| 587 | |
|---|
| 588 | sub reset_ids { |
|---|
| 589 | my $tmpl = shift; |
|---|
| 590 | $tmpl->{__ids} = undef; |
|---|
| 591 | } |
|---|
| 592 | |
|---|
| 593 | sub reset_classes { |
|---|
| 594 | my $tmpl = shift; |
|---|
| 595 | $tmpl->{__classes} = undef; |
|---|
| 596 | } |
|---|
| 597 | |
|---|
| 598 | sub reset_markers { |
|---|
| 599 | my $tmpl = shift; |
|---|
| 600 | $tmpl->{__classes} = undef; |
|---|
| 601 | $tmpl->{__ids} = undef; |
|---|
| 602 | } |
|---|
| 603 | |
|---|
| 604 | sub token_ids { |
|---|
| 605 | my $tmpl = shift; |
|---|
| 606 | if (@_) { |
|---|
| 607 | return $tmpl->{__ids} = shift; |
|---|
| 608 | } |
|---|
| 609 | $tmpl->rescan unless $tmpl->{__ids}; |
|---|
| 610 | return $tmpl->{__ids}; |
|---|
| 611 | } |
|---|
| 612 | |
|---|
| 613 | sub token_classes { |
|---|
| 614 | my $tmpl = shift; |
|---|
| 615 | if (@_) { |
|---|
| 616 | return $tmpl->{__classes} = shift; |
|---|
| 617 | } |
|---|
| 618 | $tmpl->rescan unless $tmpl->{__classes}; |
|---|
| 619 | return $tmpl->{__classes}; |
|---|
| 620 | } |
|---|
| 621 | |
|---|
| 622 | sub tokens { |
|---|
| 623 | my $tmpl = shift; |
|---|
| 624 | if (@_) { |
|---|
| 625 | return bless $tmpl->{__tokens} = shift, 'MT::Template::Tokens'; |
|---|
| 626 | } |
|---|
| 627 | my $t = $tmpl->{__tokens} || $tmpl->compile; |
|---|
| 628 | return bless $t, 'MT::Template::Tokens' if $t; |
|---|
| 629 | return undef; |
|---|
| 630 | } |
|---|
| 631 | |
|---|
| 632 | sub published_url { |
|---|
| 633 | my $tmpl = shift; |
|---|
| 634 | |
|---|
| 635 | return undef unless $tmpl->outfile; |
|---|
| 636 | return undef unless ($tmpl->type eq 'index'); |
|---|
| 637 | |
|---|
| 638 | my $blog = $tmpl->blog; |
|---|
| 639 | return undef unless $blog; |
|---|
| 640 | my $site_url = $blog->site_url || ''; |
|---|
| 641 | $site_url .= '/' if $site_url !~ m!/$!; |
|---|
| 642 | my $url = $site_url . $tmpl->outfile; |
|---|
| 643 | |
|---|
| 644 | if ($tmpl->build_dynamic) { |
|---|
| 645 | require MT::FileInfo; |
|---|
| 646 | my @finfos = MT::FileInfo->load({blog_id => $tmpl->blog_id, |
|---|
| 647 | template_id => $tmpl->id}); |
|---|
| 648 | if (scalar @finfos == 1) { |
|---|
| 649 | return $url; |
|---|
| 650 | } |
|---|
| 651 | } else { |
|---|
| 652 | my $site_path = $blog->site_path || ''; |
|---|
| 653 | my $tmpl_path = File::Spec->catfile($site_path, $tmpl->outfile); |
|---|
| 654 | if (-f $tmpl_path) { |
|---|
| 655 | return $url; |
|---|
| 656 | } |
|---|
| 657 | } |
|---|
| 658 | undef; |
|---|
| 659 | } |
|---|
| 660 | |
|---|
| 661 | sub pre_remove_children { |
|---|
| 662 | my $tmpl = shift; |
|---|
| 663 | $tmpl->remove_children({ key => 'template_id' }); |
|---|
| 664 | } |
|---|
| 665 | |
|---|
| 666 | sub post_remove_widget { |
|---|
| 667 | my $tmpl = shift; |
|---|
| 668 | return unless $tmpl->type eq 'widget'; |
|---|
| 669 | |
|---|
| 670 | my $iter = MT::Template->load_iter({ |
|---|
| 671 | blog_id => [ $tmpl->blog_id, 0 ], |
|---|
| 672 | type => 'widgetset', |
|---|
| 673 | }); |
|---|
| 674 | my @resave; |
|---|
| 675 | while ( my $ws = $iter->() ) { |
|---|
| 676 | my @mods = split( ',', $ws->modulesets ); |
|---|
| 677 | if ( grep { $_ == $tmpl->id } @mods ) { |
|---|
| 678 | push @resave, $ws; |
|---|
| 679 | } |
|---|
| 680 | } |
|---|
| 681 | $_->save for @resave; |
|---|
| 682 | } |
|---|
| 683 | __PACKAGE__->add_trigger('post_remove' => \&post_remove_widget); |
|---|
| 684 | |
|---|
| 685 | # Some DOM-inspired methods (replicating the interface, so it's more |
|---|
| 686 | # familiar to those who know DOM) |
|---|
| 687 | sub getElementsByTagName { |
|---|
| 688 | my $tmpl = shift; |
|---|
| 689 | return MT::Template::Tokens::getElementsByTagName($tmpl->tokens, @_); |
|---|
| 690 | } |
|---|
| 691 | |
|---|
| 692 | sub getElementsByClassName { |
|---|
| 693 | my $tmpl = shift; |
|---|
| 694 | my ($name) = @_; |
|---|
| 695 | my $classes = $tmpl->token_classes; |
|---|
| 696 | my $tokens = $classes->{lc $name}; |
|---|
| 697 | if ($tokens && @$tokens) { |
|---|
| 698 | #@$tokens = map { bless $_, NODE } @$tokens; |
|---|
| 699 | return @$tokens; |
|---|
| 700 | } |
|---|
| 701 | return (); |
|---|
| 702 | } |
|---|
| 703 | |
|---|
| 704 | sub getElementsByName { |
|---|
| 705 | my $tmpl = shift; |
|---|
| 706 | return MT::Template::Tokens::getElementsByName($tmpl->tokens, @_); |
|---|
| 707 | } |
|---|
| 708 | |
|---|
| 709 | sub getElementById { |
|---|
| 710 | my $tmpl = shift; |
|---|
| 711 | my ($id) = @_; |
|---|
| 712 | if (my $node = $tmpl->token_ids->{$id}) { |
|---|
| 713 | return $node; |
|---|
| 714 | } |
|---|
| 715 | undef; |
|---|
| 716 | } |
|---|
| 717 | |
|---|
| 718 | sub createElement { |
|---|
| 719 | my $tmpl = shift; |
|---|
| 720 | my ($tag, $attr) = @_; |
|---|
| 721 | return NODE->new(tag => $tag, attributes => $attr, template => $tmpl); |
|---|
| 722 | } |
|---|
| 723 | |
|---|
| 724 | sub createTextNode { |
|---|
| 725 | my $tmpl = shift; |
|---|
| 726 | my ($text) = @_; |
|---|
| 727 | return NODE->new(tag => 'TEXT', nodeValue => $text, template => $tmpl); |
|---|
| 728 | } |
|---|
| 729 | |
|---|
| 730 | sub insertAfter { |
|---|
| 731 | my $tmpl = shift; |
|---|
| 732 | my ($node1, $node2) = @_; |
|---|
| 733 | my $parent_node = $node2 ? $node2->parentNode : $tmpl; |
|---|
| 734 | my $parent_array = $parent_node->childNodes; |
|---|
| 735 | if ( $node2 ) { |
|---|
| 736 | for (my $i = 0; $i < scalar @$parent_array; $i++) { |
|---|
| 737 | if ($parent_array->[$i] == $node2) { |
|---|
| 738 | $node1->parentNode($parent_node); |
|---|
| 739 | splice(@$parent_array, $i + 1, 0, $node1); |
|---|
| 740 | return 1; |
|---|
| 741 | } |
|---|
| 742 | } |
|---|
| 743 | return 0; |
|---|
| 744 | } |
|---|
| 745 | else { |
|---|
| 746 | $node1->parentNode($parent_node); |
|---|
| 747 | push @$parent_array, $node1; |
|---|
| 748 | return 1; |
|---|
| 749 | } |
|---|
| 750 | return 0; |
|---|
| 751 | } |
|---|
| 752 | |
|---|
| 753 | sub insertBefore { |
|---|
| 754 | my $tmpl = shift; |
|---|
| 755 | my ($node1, $node2) = @_; |
|---|
| 756 | my $parent_node = $node2 ? $node2->parentNode : $tmpl; |
|---|
| 757 | my $parent_array = $parent_node->childNodes; |
|---|
| 758 | if ( $node2 ) { |
|---|
| 759 | for (my $i = 0; $i < scalar @$parent_array; $i++) { |
|---|
| 760 | if ($parent_array->[$i] == $node2) { |
|---|
| 761 | $node1->parentNode($parent_node); |
|---|
| 762 | splice(@$parent_array, $i, 0, $node1); |
|---|
| 763 | return 1; |
|---|
| 764 | } |
|---|
| 765 | } |
|---|
| 766 | return 0; |
|---|
| 767 | } |
|---|
| 768 | else { |
|---|
| 769 | $node1->parentNode($parent_node); |
|---|
| 770 | unshift @$parent_array, $node1; |
|---|
| 771 | return 1; |
|---|
| 772 | } |
|---|
| 773 | return 0; |
|---|
| 774 | } |
|---|
| 775 | |
|---|
| 776 | sub childNodes { |
|---|
| 777 | my $tmpl = shift; |
|---|
| 778 | return $tmpl->tokens; |
|---|
| 779 | } |
|---|
| 780 | |
|---|
| 781 | sub hasChildNodes { |
|---|
| 782 | my $tmpl = shift; |
|---|
| 783 | my $nodes = $tmpl->childNodes; |
|---|
| 784 | return $nodes && (@$nodes) ? 1 : 0; |
|---|
| 785 | } |
|---|
| 786 | |
|---|
| 787 | sub appendChild { |
|---|
| 788 | my $tmpl = shift; |
|---|
| 789 | my ($new_node) = @_; |
|---|
| 790 | my $nodes = $tmpl->childNodes; |
|---|
| 791 | push @$nodes, $new_node; |
|---|
| 792 | $tmpl->{reflow_flag} = 1; |
|---|
| 793 | } |
|---|
| 794 | |
|---|
| 795 | # Alias to perl_function_names for those that may prefer that. |
|---|
| 796 | # *get_elements_by_tag_name = \&getElementsByTagName; |
|---|
| 797 | # *get_elements_by_name = \&getElementsByName; |
|---|
| 798 | # *get_element_by_id = \&getElementById; |
|---|
| 799 | # *create_element = \&createElement; |
|---|
| 800 | |
|---|
| 801 | # functionality for individual nodes gathered by DOM-like query operations. |
|---|
| 802 | |
|---|
| 803 | package MT::Template::Tokens; |
|---|
| 804 | |
|---|
| 805 | use strict; |
|---|
| 806 | |
|---|
| 807 | sub getElementsByTagName { |
|---|
| 808 | my ($tokens, $name) = @_; |
|---|
| 809 | my @list; |
|---|
| 810 | $name = lc $name; |
|---|
| 811 | foreach my $t (@$tokens) { |
|---|
| 812 | if (lc $t->tag eq $name) { |
|---|
| 813 | push @list, $t; |
|---|
| 814 | } |
|---|
| 815 | if (my $childNodes = $t->childNodes) { |
|---|
| 816 | my $subt = getElementsByTagName($childNodes, $name); |
|---|
| 817 | push @list, @$subt if $subt; |
|---|
| 818 | } |
|---|
| 819 | } |
|---|
| 820 | scalar @list ? \@list : undef; |
|---|
| 821 | } |
|---|
| 822 | |
|---|
| 823 | sub getElementsByName { |
|---|
| 824 | my ($tokens, $name) = @_; |
|---|
| 825 | my @list; |
|---|
| 826 | $name = lc $name; |
|---|
| 827 | foreach my $t (@$tokens) { |
|---|
| 828 | if (lc ($t->getAttribute('name') || '') eq $name) { |
|---|
| 829 | push @list, $t; |
|---|
| 830 | } |
|---|
| 831 | if (my $childNodes = $t->childNodes) { |
|---|
| 832 | my $subt = getElementsByName($childNodes, $name); |
|---|
| 833 | push @list, @$subt if $subt; |
|---|
| 834 | } |
|---|
| 835 | } |
|---|
| 836 | scalar @list ? \@list : undef; |
|---|
| 837 | } |
|---|
| 838 | |
|---|
| 839 | # trans('Index') |
|---|
| 840 | # trans('Archive') |
|---|
| 841 | # trans('Category Archive') |
|---|
| 842 | # trans('Individual') |
|---|
| 843 | # trans('Page') |
|---|
| 844 | # trans('Comment Listing') |
|---|
| 845 | # trans('Ping Listing') |
|---|
| 846 | # trans('Comment Preview') |
|---|
| 847 | # trans('Comment Error') |
|---|
| 848 | # trans('Comment Pending') |
|---|
| 849 | # trans('Dynamic Error') |
|---|
| 850 | # trans('Uploaded Image') |
|---|
| 851 | # trans('Search Results') |
|---|
| 852 | # trans('Module') |
|---|
| 853 | # trans('Widget') |
|---|
| 854 | |
|---|
| 855 | 1; |
|---|
| 856 | __END__ |
|---|
| 857 | |
|---|
| 858 | =head1 NAME |
|---|
| 859 | |
|---|
| 860 | MT::Template - Movable Type template record |
|---|
| 861 | |
|---|
| 862 | =head1 SYNOPSIS |
|---|
| 863 | |
|---|
| 864 | use MT::Template; |
|---|
| 865 | my $tmpl = MT::Template->load($tmpl_id); |
|---|
| 866 | defined(my $html = $tmpl->build($ctx)) |
|---|
| 867 | or die $tmpl->errstr; |
|---|
| 868 | |
|---|
| 869 | $tmpl->name('New Template name'); |
|---|
| 870 | $tmpl->save |
|---|
| 871 | or die $tmpl->errstr; |
|---|
| 872 | |
|---|
| 873 | =head1 DESCRIPTION |
|---|
| 874 | |
|---|
| 875 | An I<MT::Template> object represents a template in the Movable Type system. |
|---|
| 876 | It contains the actual template body, along with metadata used for keeping |
|---|
| 877 | the template in sync with a linked file, etc. It also contains the |
|---|
| 878 | functionality necessary to build an output file from a generic template. |
|---|
| 879 | |
|---|
| 880 | Linking a template to an external file means that any updates to the template |
|---|
| 881 | through the Movable Type CMS will be synced automatically to the file on |
|---|
| 882 | disk, and vice versa. This allows authors to edit their templates in an |
|---|
| 883 | external editor that supports FTP, which is preferable for users who do not |
|---|
| 884 | like editing in textareas. |
|---|
| 885 | |
|---|
| 886 | =head1 USAGE |
|---|
| 887 | |
|---|
| 888 | As a subclass of I<MT::Object>, I<MT::Template> inherits all of the |
|---|
| 889 | data-management and -storage methods from that class; thus you should look |
|---|
| 890 | at the I<MT::Object> documentation for details about creating a new object, |
|---|
| 891 | loading an existing object, saving an object, etc. |
|---|
| 892 | |
|---|
| 893 | The following methods are unique to the I<MT::Template> interface: |
|---|
| 894 | |
|---|
| 895 | =head2 $tmpl->build($ctx [, \%cond ]) |
|---|
| 896 | |
|---|
| 897 | Given a context I<$ctx> (an I<MT::Template::Context> object) and an optional |
|---|
| 898 | set of conditions \%cond, builds a template into its output form. The |
|---|
| 899 | template is first parsed into a list of tokens, then is interpreted/executed |
|---|
| 900 | to generate the final output. |
|---|
| 901 | |
|---|
| 902 | If specified, I<\%cond> should be a reference to a hash with MT tag names |
|---|
| 903 | (without the leading C<MT>) as the keys, and boolean flags as the values--the |
|---|
| 904 | flags specify whether the template interpreter should include any |
|---|
| 905 | conditional containers found in the template body. |
|---|
| 906 | |
|---|
| 907 | Returns the output as a scalar string, C<undef> on error. Because the empty |
|---|
| 908 | string C<''> and the value C<0> are both valid return values for this method, |
|---|
| 909 | you should check specifically for C<undef>: |
|---|
| 910 | |
|---|
| 911 | defined(my $html = $tmpl->build($ctx)) |
|---|
| 912 | or die $tmpl->errstr; |
|---|
| 913 | |
|---|
| 914 | =head2 $tmpl->published_url |
|---|
| 915 | |
|---|
| 916 | Only applicable if the template is an Index Template, this method returns |
|---|
| 917 | the url for the page which is the result of building the index template. |
|---|
| 918 | If the template is not of type index, or if the index template has not built |
|---|
| 919 | yet (if the template is static), or if the index template does not have |
|---|
| 920 | corresponding FileInfo record (if the template is dynamic), this method |
|---|
| 921 | returns undef. |
|---|
| 922 | |
|---|
| 923 | =head2 $tmpl->blog |
|---|
| 924 | |
|---|
| 925 | Returns the I<MT::Blog> object associated with the template. |
|---|
| 926 | |
|---|
| 927 | =head2 $tmpl->save |
|---|
| 928 | |
|---|
| 929 | Saves the template and if linked to a physical file, updates the file. |
|---|
| 930 | |
|---|
| 931 | =head2 $tmpl->remove |
|---|
| 932 | |
|---|
| 933 | Removes the template object and any related objects in I<MT::TemplateMap> |
|---|
| 934 | and I<MT::FileInfo>. |
|---|
| 935 | |
|---|
| 936 | =head2 $tmpl->set_values |
|---|
| 937 | |
|---|
| 938 | Updates the values of the C<$tmpl> object. When this is called through |
|---|
| 939 | the I<MT::ObjectDriver> classes (upon loading a template object), and if |
|---|
| 940 | the template is linked to a file, it will also load the contents of that |
|---|
| 941 | file, setting the 'text' property. |
|---|
| 942 | |
|---|
| 943 | =head1 DATA ACCESS METHODS |
|---|
| 944 | |
|---|
| 945 | The I<MT::Template> object holds the following pieces of data. These fields |
|---|
| 946 | can be accessed and set using the standard data access methods described in |
|---|
| 947 | the I<MT::Object> documentation. |
|---|
| 948 | |
|---|
| 949 | =over 4 |
|---|
| 950 | |
|---|
| 951 | =item * id |
|---|
| 952 | |
|---|
| 953 | The numeric ID of the template. |
|---|
| 954 | |
|---|
| 955 | =item * blog_id |
|---|
| 956 | |
|---|
| 957 | The numeric ID of the blog to which this template belongs. |
|---|
| 958 | |
|---|
| 959 | =item * name |
|---|
| 960 | |
|---|
| 961 | The name of the template. This should be unique on a per-blog basis, because |
|---|
| 962 | templates--particularly template modules, which are stored as regular |
|---|
| 963 | templates--are included by name, using C<E<lt>MTIncludeE<gt>>. |
|---|
| 964 | |
|---|
| 965 | =item * type |
|---|
| 966 | |
|---|
| 967 | The type of the template. This can be any of the following values: C<index>, |
|---|
| 968 | an Index Template; C<archive>, an Archive Template; C<category>, also an |
|---|
| 969 | Archive Template; C<individual>, also an Archive Template; C<comments>, a |
|---|
| 970 | Comment Listing Template; C<comment_preview>, a Comment Preview Template; |
|---|
| 971 | C<comment_error>, a Comment Error Template; C<popup_image>, an Uploaded Image |
|---|
| 972 | Popup Template; or C<custom>, a Template Module. |
|---|
| 973 | |
|---|
| 974 | =item * outfile |
|---|
| 975 | |
|---|
| 976 | The name/path of the output file of this template. This is only applicable |
|---|
| 977 | if the template is an Index Template. |
|---|
| 978 | |
|---|
| 979 | =item * text |
|---|
| 980 | |
|---|
| 981 | The body of the template, containing the markup and Movable Type template |
|---|
| 982 | tags. |
|---|
| 983 | |
|---|
| 984 | If the template is linked to an external file, the body of the template is |
|---|
| 985 | automatically synced between this data field and the external file. |
|---|
| 986 | |
|---|
| 987 | =item * rebuild_me |
|---|
| 988 | |
|---|
| 989 | A boolean flag specifying whether or not the index template will be rebuilt |
|---|
| 990 | automatically when rebuilding indexes, rebuilding all, or saving a new entry. |
|---|
| 991 | |
|---|
| 992 | =item * linked_file |
|---|
| 993 | |
|---|
| 994 | The name/path of the file to which this template is linked in the filesystem, |
|---|
| 995 | if it is linked. |
|---|
| 996 | |
|---|
| 997 | =item * linked_file_mtime |
|---|
| 998 | |
|---|
| 999 | The last modified time of the linked file. This, along with |
|---|
| 1000 | I<linked_file_size>, is used to determine whether a file has been updated on |
|---|
| 1001 | disk, and needs to be re-synced. |
|---|
| 1002 | |
|---|
| 1003 | =item * linked_file_size |
|---|
| 1004 | |
|---|
| 1005 | The size of the linked file, in bytes. This, along with I<linked_file_mtime>, |
|---|
| 1006 | is used to determine whether a file has been updated on disk, and needs to |
|---|
| 1007 | be re-synced. |
|---|
| 1008 | |
|---|
| 1009 | =back |
|---|
| 1010 | |
|---|
| 1011 | =head1 DATA LOOKUP |
|---|
| 1012 | |
|---|
| 1013 | In addition to numeric ID lookup, you can look up or sort records by any |
|---|
| 1014 | combination of the following fields. See the I<load> documentation in |
|---|
| 1015 | I<MT::Object> for more information. |
|---|
| 1016 | |
|---|
| 1017 | =over 4 |
|---|
| 1018 | |
|---|
| 1019 | =item * blog_id |
|---|
| 1020 | |
|---|
| 1021 | =item * name |
|---|
| 1022 | |
|---|
| 1023 | =item * type |
|---|
| 1024 | |
|---|
| 1025 | =back |
|---|
| 1026 | |
|---|
| 1027 | =head1 NOTES |
|---|
| 1028 | |
|---|
| 1029 | =over 4 |
|---|
| 1030 | |
|---|
| 1031 | =item * |
|---|
| 1032 | |
|---|
| 1033 | When you remove a template using I<MT::Template::remove>, in addition to |
|---|
| 1034 | removing the template record, all of the I<MT::TemplateMap> objects |
|---|
| 1035 | associated with this template will be removed. |
|---|
| 1036 | |
|---|
| 1037 | =item * |
|---|
| 1038 | |
|---|
| 1039 | If a template is linked to an external file, I<MT::Template::save> will sync |
|---|
| 1040 | the template body to disk, and I<MT::Template::text> (the data access method |
|---|
| 1041 | to retrieve the body of the template) will sync the template body from disk. |
|---|
| 1042 | |
|---|
| 1043 | =back |
|---|
| 1044 | |
|---|
| 1045 | =head1 AUTHOR & COPYRIGHTS |
|---|
| 1046 | |
|---|
| 1047 | Please see the I<MT> manpage for author, copyright, and license information. |
|---|
| 1048 | |
|---|
| 1049 | =cut |
|---|