Changeset 1927
- Timestamp:
- 04/16/08 15:36:30 (22 months ago)
- Location:
- branches/release-35
- Files:
-
- 15 modified
- 10 copied
-
build/mt-dists/default.mk (modified) (1 diff)
-
lib/MT.pm.pre (modified) (9 diffs)
-
lib/MT/Asset/Image.pm (modified) (1 diff)
-
lib/MT/Author.pm (modified) (3 diffs)
-
lib/MT/Blog.pm (modified) (2 diffs)
-
lib/MT/Component.pm (modified) (1 diff)
-
lib/MT/Meta (copied) (copied from branches/feature-narrow-tables/lib/MT/Meta)
-
lib/MT/Meta.pm (copied) (copied from branches/feature-narrow-tables/lib/MT/Meta.pm)
-
lib/MT/Meta/Proxy.pm (copied) (copied from branches/feature-narrow-tables/lib/MT/Meta/Proxy.pm)
-
lib/MT/Object.pm (modified) (14 diffs)
-
lib/MT/ObjectDriver/DDL/mysql.pm (modified) (1 diff)
-
lib/MT/Template.pm (modified) (2 diffs)
-
lib/MT/Upgrade.pm (modified) (1 diff)
-
t/09-image.t (modified) (4 diffs)
-
t/20-setup.t (modified) (1 diff)
-
t/22-author.t (modified) (4 diffs)
-
t/64-objectmeta.t (modified) (2 diffs)
-
t/lib/MT/Test.pm (modified) (31 diffs)
-
t/plugins/Awesome (copied) (copied from branches/feature-narrow-tables/t/plugins/Awesome)
-
t/plugins/Awesome/config.yaml (copied) (copied from branches/feature-narrow-tables/t/plugins/Awesome/config.yaml)
-
t/plugins/Awesome/lib (copied) (copied from branches/feature-narrow-tables/t/plugins/Awesome/lib)
-
t/plugins/Awesome/lib/MT (copied) (copied from branches/feature-narrow-tables/t/plugins/Awesome/lib/MT)
-
t/plugins/Awesome/lib/MT/Awesome (copied) (copied from branches/feature-narrow-tables/t/plugins/Awesome/lib/MT/Awesome)
-
t/plugins/Awesome/lib/MT/Awesome.pm (copied) (copied from branches/feature-narrow-tables/t/plugins/Awesome/lib/MT/Awesome.pm)
-
t/plugins/Awesome/lib/MT/Awesome/Image.pm (copied) (copied from branches/feature-narrow-tables/t/plugins/Awesome/lib/MT/Awesome/Image.pm)
Legend:
- Unmodified
- Added
- Removed
-
branches/release-35/build/mt-dists/default.mk
r1897 r1927 2 2 3 3 PRODUCT_VERSION = 4.15 4 SCHEMA_VERSION = 4.005 44 SCHEMA_VERSION = 4.0055 5 5 API_VERSION = 4.15 6 6 -
branches/release-35/lib/MT.pm.pre
r1871 r1927 58 58 no strict 'refs'; 59 59 unless ( defined *{ $compat . '::' } ) { 60 eval " require $compat;";60 eval "# line " . __LINE__ . " " . __FILE__ . "\nrequire $compat;"; 61 61 } 62 62 } … … 267 267 return $object_types{$k} if exists $object_types{$k}; 268 268 269 if ($k =~ m/^(.+):meta$/) { 270 my $ppkg = $pkg->model($1); 271 my $mpkg = $ppkg->meta_pkg; 272 return $mpkg ? $object_types{$k} = $mpkg : undef; 273 } 274 269 275 my $model = $pkg->registry( 'object_types', $k ); 270 276 if ( ref($model) eq 'ARRAY' ) { … … 287 293 unless ( defined *{ $model . '::__properties' } ) { 288 294 use strict 'refs'; 289 eval " require $model;";295 eval "# line " . __LINE__ . " " . __FILE__ . "\nrequire $model;"; 290 296 if ( $@ && ( $k =~ m/^(.+)\./ ) ) { 291 297 … … 612 618 613 619 $cb->error(); # reset the error string 614 my $result = eval { $cb->invoke(@args) }; 620 my $result = eval { 621 # line __LINE__ __FILE__ 622 $cb->invoke(@args); 623 }; 615 624 if ( my $err = $@ ) { 616 625 $cb->error($err); … … 1262 1271 $Plugins{$plugin_sig}{full_path} = $plugin_full_path; 1263 1272 $timer->pause_partial if $timer; 1264 eval { require $plugin };1273 eval "# line " . __LINE__ . " " . __FILE__ . "\nrequire '$plugin';"; 1265 1274 $timer->mark("Loaded plugin " . $sig) if $timer; 1266 1275 if ($@) { … … 1270 1279 # the database has been initialized... 1271 1280 eval { 1281 # line __LINE__ __FILE__ 1272 1282 require MT::Log; 1273 1283 $mt->log( … … 2588 2598 if ($method) { 2589 2599 return sub { 2590 eval " require $hdlr_pkg;"2600 eval "# line " . __LINE__ . " " . __FILE__ . "\nrequire $hdlr_pkg;" 2591 2601 or Carp::confess( 2592 2602 "failed loading package $hdlr_pkg for routine $name: $@"); … … 2599 2609 else { 2600 2610 return sub { 2601 eval " require $hdlr_pkg;"2611 eval "# line " . __LINE__ . " " . __FILE__ . "\nrequire $hdlr_pkg;" 2602 2612 or Carp::confess( 2603 2613 "failed loading package $hdlr_pkg for routine $name: $@"); … … 2613 2623 } 2614 2624 else { 2615 eval " require $hdlr_pkg;"2625 eval "# line " . __LINE__ . " " . __FILE__ . "\nrequire $hdlr_pkg;" 2616 2626 or Carp::confess( 2617 2627 "failed loading package $hdlr_pkg for routine $name: $@"); -
branches/release-35/lib/MT/Asset/Image.pm
r1829 r1927 10 10 use base qw( MT::Asset ); 11 11 12 __PACKAGE__->install_properties( { class_type => 'image', } ); 13 __PACKAGE__->install_meta( { columns => [ 'image_width', 'image_height', ], } ); 12 __PACKAGE__->install_properties( { 13 class_type => 'image', 14 column_defs => { 15 'image_width' => 'integer meta', 16 'image_height' => 'integer meta', 17 }, 18 } ); 14 19 15 20 # List of supported file extensions (to aid the stock 'can_handle' method.) -
branches/release-35/lib/MT/Author.pm
r1823 r1927 37 37 'userpic_asset_id' => 'integer', 38 38 'basename' => 'string(255)', 39 40 # meta properties 41 'widgets' => 'hash meta', 42 'favorite_blogs' => 'array meta', 39 43 }, 40 44 defaults => { … … 59 63 audit => 1, 60 64 }); 61 __PACKAGE__->install_meta({62 columns => [63 'favorite_blogs',64 'widgets',65 ],66 });67 65 68 66 sub class_label { … … 263 261 die unless $perms->isa('MT::Permission'); 264 262 $perms->can_edit_all_posts || 265 ($perms->can_create_post && $entry->author_id == $author->id);263 ($perms->can_create_post && $entry->author_id == $author->id); 266 264 } 267 265 -
branches/release-35/lib/MT/Blog.pm
r1868 r1927 77 77 'archive_tmpl_category' => 'string(255)', 78 78 'archive_tmpl_individual' => 'string(255)', 79 80 # meta properties 81 'image_default_wrap_text' => 'integer meta', 82 'image_default_align' => 'string meta', 83 'image_default_thumb' => 'integer meta', 84 'image_default_width' => 'integer meta', 85 'image_default_wunits' => 'integer meta', 86 'image_default_constrain' => 'integer meta', 87 'image_default_popup' => 'integer meta', 88 'commenter_authenticators' => 'string meta', 89 'require_typekey_emails' => 'integer meta', 90 'nofollow_urls' => 'integer meta', 91 'follow_auth_links' => 'integer meta', 92 'update_pings' => 'integer meta', 93 'captcha_provider' => 'string meta', 94 'publish_queue' => 'integer meta', 95 'nwc_smart_replace' => 'integer meta', 96 'nwc_replace_field' => 'string meta', 97 'template_set' => 'string meta', 98 'page_layout' => 'string meta', 99 'include_system' => 'integer meta', 100 'include_cache' => 'integer meta', 79 101 }, 80 102 meta => 1, … … 93 115 datasource => 'blog', 94 116 primary_key => 'id', 95 });96 __PACKAGE__->install_meta({97 columns => [98 'image_default_wrap_text',99 'image_default_align',100 'image_default_thumb',101 'image_default_width',102 'image_default_wunits',103 'image_default_constrain',104 'image_default_popup',105 'commenter_authenticators',106 'require_typekey_emails',107 'nofollow_urls',108 'follow_auth_links',109 'update_pings',110 'captcha_provider',111 'publish_queue',112 'nwc_smart_replace',113 'nwc_replace_field',114 'template_set',115 'page_layout',116 'include_system',117 'include_cache',118 ],119 117 }); 120 118 -
branches/release-35/lib/MT/Component.pm
r1572 r1927 508 508 509 509 # check for a yaml file reference... 510 if ( !ref($v) && ( $v =~ m/^[-\w]+\.yaml$/ ) ) { 511 my $f = File::Spec->catfile( $c->path, $v ); 512 if ( -f $f ) { 513 require YAML::Tiny; 514 my $y = eval { YAML::Tiny->read($f) } 515 or die "Error reading $f: " . $YAML::Tiny::errstr; 516 517 # skip over non-hash elements 518 shift @$y while @$y && ( ref( $y->[0] ) ne 'HASH' ); 519 if (@$y) { 520 $r->{$p} = $y->[0]; 510 if ( !ref($v) ) { 511 if ( $v =~ m/^[-\w]+\.yaml$/ ) { 512 my $f = File::Spec->catfile( $c->path, $v ); 513 if ( -f $f ) { 514 require YAML::Tiny; 515 my $y = eval { YAML::Tiny->read($f) } 516 or die "Error reading $f: " 517 . $YAML::Tiny::errstr; 518 # skip over non-hash elements 519 shift @$y 520 while @$y && ( ref( $y->[0] ) ne 'HASH' ); 521 $r->{$p} = $y->[0] if @$y; 522 } 523 } elsif ($v =~ m/^\$\w+::/) { 524 my $code = MT->handler_to_coderef($v); 525 if (ref $code eq 'CODE') { 526 $r->{$p} = $code->($c); 521 527 } 522 528 } -
branches/release-35/lib/MT/Object.pm
r1873 r1927 53 53 } 54 54 55 my %meta; 56 55 57 my $super_props = $class->SUPER::properties(); 58 $props->{meta} = 1 if $super_props && $super_props->{meta}; 59 60 if ($props->{meta}) { 61 # yank out any meta columns before we start working on column_defs 62 $meta{$_} = delete $props->{column_defs}{$_} 63 for grep { $props->{column_defs}{$_} =~ m/\bmeta\b/ } 64 keys %{ $props->{column_defs} }; 65 } 66 56 67 if ($super_props) { 57 68 # subclass; merge hash 58 for (qw(primary_key meta_column class_column datasource driver audit meta)) {69 for (qw(primary_key class_column datasource driver audit)) { 59 70 $props->{$_} = $super_props->{$_} 60 71 if exists $super_props->{$_} && !(exists $props->{$_}); 61 72 } 62 for my $p (qw(column_defs defaults indexes meta_columns)) {73 for my $p (qw(column_defs defaults indexes)) { 63 74 if (exists $super_props->{$p}) { 64 75 foreach my $k (keys %{ $super_props->{$p} }) { … … 81 92 # Legacy MT::Object types only define 'columns'; we still support that 82 93 # but they aren't handled properly with the upgrade system as a result. 83 if (exists $props->{column_defs}) { 84 $props->{columns} = [ keys %{ $props->{column_defs} } ]; 85 } else { 94 if (! exists $props->{column_defs}) { 86 95 map { $props->{column_defs}{$_} = () } @{ $props->{columns} }; 87 96 } 97 $props->{columns} = [ keys %{ $props->{column_defs} } ]; 88 98 89 99 # Support audit flags … … 98 108 } 99 109 100 # Metadata column101 $props->{meta_column} ||= 'meta' if exists $props->{meta};102 if (my $col = $props->{meta_column}) {103 if (!$props->{column_defs}{$col}) {104 $props->{column_defs}{$col} = 'blob';105 push @{ $props->{columns} }, $col;106 }107 no strict 'refs'; ## no critic108 *{$class . '::' . $col} = \&__meta_column;109 $class->add_trigger( pre_save => \&pre_save_serialize_metadata );110 }111 112 110 # Classed object types 113 111 $props->{class_column} ||= 'class' if exists $props->{class_type}; … … 151 149 foreach my $isa_class (@classes) { 152 150 next if UNIVERSAL::isa($class, $isa_class); 153 eval " require $isa_class;" or die;151 eval "# line " . __LINE__ . " " . __FILE__ . "\nrequire $isa_class;" or die; 154 152 no strict 'refs'; ## no critic 155 153 push @{$class . '::ISA'}, $isa_class; … … 160 158 for my $name (keys %$cols) { 161 159 next if exists $props->{column_defs}{$name}; 160 if ($cols->{$name} =~ m/\bmeta\b/) { 161 $meta{$name} = $cols->{$name}; 162 next; 163 } 164 162 165 $class->install_column($name, $cols->{$name}); 163 166 $props->{indexes}{$name} = 1 … … 226 229 no strict 'refs'; ## no critic 227 230 *{$class . '::driver'} = sub { $_[0]->dbi_driver(@_) }; 231 } 232 233 # inherit parent's metadata setup 234 if ($props->{meta}) { # if ($super_props && $super_props->{meta_installed}) { 235 $class->install_meta({ ( %meta ? ( column_defs => \%meta ) : ( columns => [] ) ) }); 236 $class->add_trigger( post_remove => \&remove_meta ); 228 237 } 229 238 … … 347 356 return $package; 348 357 } else { 349 eval " use $package;";358 eval "# line " . __LINE__ . " " . __FILE__ . "\nuse $package;"; 350 359 return $package unless $@; 351 eval " use $pkg; $package->new;";360 eval "# line " . __LINE__ . " " . __FILE__ . "\nuse $pkg; $package->new;"; 352 361 return $package unless $@; 353 362 } … … 373 382 # 'meta' metadata column support 374 383 384 sub new { 385 my $class = shift; 386 my $obj = $class->SUPER::new(@_); 387 if ($obj->properties->{meta_installed}) { 388 $obj->init_meta(); 389 } 390 return $obj; 391 } 392 393 sub init_meta { 394 my $obj = shift; 395 require MT::Meta::Proxy; 396 $obj->{__meta} = MT::Meta::Proxy->new($obj); 397 } 398 375 399 sub install_meta { 376 400 my $class = shift; 377 my ($p rops) = @_;401 my ($params) = @_; 378 402 if ( ( $class ne 'MT::Config' ) && (!$MT::plugins_installed) ) { 379 push @PRE_INIT_META, [$class, $p rops];403 push @PRE_INIT_META, [$class, $params]; 380 404 return; 381 405 } 382 my $cprops = $class->properties; 383 my $fields = $cprops->{meta_columns} ||= {}; 384 my $meta_col = $cprops->{meta_column}; 385 foreach my $name (@{ $props->{columns} }) { 386 $fields->{$name} = (); 387 # Skip adding this method if the class overloads it. 388 # this lets the SUPER::columnname magic do it's thing 389 unless ($class->can($name)) { 390 no strict 'refs'; ## no critic 391 *{"${class}::$name"} = sub { shift->$meta_col($name, @_) }; 392 } 393 } 406 407 require MT::Meta; 408 my $pkg = ref $class || $class; 409 if (!$pkg->SUPER::properties->{meta_installed}) { 410 $pkg->add_trigger( post_save => \&post_save_save_metadata ); 411 $pkg->add_trigger( post_load => \&post_load_initialize_metadata ); 412 } 413 414 my $props = $class->properties; 415 416 if (!$params->{columns} && !$params->{fields} && !$params->{column_defs}) { 417 return $class->error('No meta fields specified to install_meta'); 418 } 419 420 $params->{fields} ||= []; 421 if (my $cols = delete $params->{columns}) { 422 foreach my $col (@$cols) { 423 push @{ $params->{fields} }, { 424 name => $col, 425 type => 'vblob', 426 }; 427 # $props->{fields}{$col} = 'vblob'; 428 } 429 } 430 if (my $cols = delete $params->{column_defs}) { 431 foreach my $col ( keys %$cols ) { 432 my $type = $cols->{$col}; 433 $type =~ s/\s.*//; # take first keyword, ignoring anything after 434 $type .= '_indexed' 435 if $cols->{$col} =~ m/\bindexed\b/; 436 $type = MT::Meta->normalize_type($type); 437 438 push @{ $params->{fields} }, { 439 name => $col, 440 type => $type, 441 }; 442 # $props->{fields}{$col} = $type; 443 } 444 } 445 446 $params->{datasource} ||= $class->datasource . '_meta'; 447 448 if ($props->{meta_installed} && !@{ $params->{fields} }) { 449 return 1; 450 } 451 452 if (my $fields = MT::Meta->install($pkg, $params)) { 453 # we may have inherited meta fields so lets update with 454 # the fields returned by MT::Meta 455 $props->{fields} = $fields; 456 } 457 458 return $props->{meta_installed} = 1; 459 } 460 461 sub meta_args { 462 my $class = shift; 463 my $id_field = $class->datasource . '_id'; 464 return { 465 key => $class->datasource, 466 column_defs => { 467 $id_field => 'integer not null', 468 type => 'string(255) not null', 469 vchar => 'string(255)', 470 vchar_indexed => 'string(255)', 471 vdatetime => 'datetime', 472 vdatetime_indexed => 'datetime', 473 vinteger => 'integer', 474 vinteger_indexed => 'integer', 475 vfloat => 'float', 476 vfloat_indexed => 'float', 477 vblob => 'blob', 478 vclob => 'text', 479 }, 480 columns => [ $id_field, qw( 481 type 482 vchar 483 vchar_indexed 484 vdatetime 485 vdatetime_indexed 486 vinteger 487 vinteger_indexed 488 vfloat 489 vfloat_indexed 490 vblob 491 vclob 492 ) ], 493 indexes => { 494 $id_field => 1, 495 id_type => { columns => [ $id_field, 'type' ] }, 496 id_type_vchar => { columns => [ $id_field, 'type', 'vchar_indexed' ] }, 497 id_type_vdatetime => { columns => [ $id_field, 'type', 498 'vdatetime_indexed' ] }, 499 id_type_vinteger => { columns => [ $id_field, 'type', 500 'vinteger_indexed' ] }, 501 id_type_vfloat => { columns => [ $id_field, 'type', 502 'vfloat_indexed' ] }, 503 }, 504 primary_key => [ $class->datasource . '_id', 'type' ], 505 }; 394 506 } 395 507 396 508 sub has_meta { 397 my $props = $_[0]->properties; 398 return $props->{meta} && (@_ > 1 ? exists $props->{meta_columns}{$_[1]} : 1); 399 } 400 401 sub pre_save_serialize_metadata { 402 my ($obj) = shift; 403 my $meta_col = $obj->properties->{meta_column}; 404 if ($obj->{changed_cols}{$meta_col}) { 405 require MT::Serialize; 406 my $meta = $obj->$meta_col; 407 $obj->$meta_col(MT::Serialize->serialize(\$meta)); 408 } 409 } 410 411 sub __thaw_meta { 412 my ($meta) = @_; 413 $$meta = '' unless defined $$meta; 414 require MT::Serialize; 415 my $out = MT::Serialize->unserialize($$meta); 416 if (ref $out eq 'REF') { 417 return $$out; 418 } else { 419 return {}; 420 } 421 } 422 423 # $obj->meta returns a hashref of metadata information 424 # $obj->meta($scalar) allows assignment of a serialized value 425 # $obj->meta('name', 'value') assigns an individual metadata element 426 # $obj->meta('name') returns an individual metadata value 427 # $obj->save will automatically serialize the metadata back to the database 428 sub __meta_column { 429 my $obj = shift; 430 my $meta_col = $obj->properties->{meta_column} or return; 431 432 if (@_) { 433 my $var = shift; 434 if ((defined $var) && ($var =~ m/^SERG\0\0\0\0/)) { 435 return $obj->column($meta_col, $var); 436 } 437 my $meta = $obj->column($meta_col); 438 if (!defined($meta)) { 439 $obj->{column_values}{$meta_col} = $meta = {}; 440 } elsif (!ref $meta) { 441 $obj->{column_values}{$meta_col} = $meta = __thaw_meta(\$meta); 442 } 509 my $obj = shift; 510 return $obj->is_meta_column(@_) if @_; 511 return $obj->properties->{meta_installed} ? 1 : 0; 512 } 513 514 sub post_load_initialize_metadata { 515 my $obj = shift; 516 if (defined $obj && $obj->properties->{meta_installed}) { 517 $obj->init_meta(); 518 $obj->{__meta}->set_primary_keys($obj); 519 } 520 } 521 522 sub is_meta_column { 523 my $obj = shift; 524 my ($field) = @_; 525 526 my $props = $obj->properties; 527 return unless $props->{meta_installed}; 528 529 my $meta = $obj->meta_pkg; 530 return 1 if $props->{fields}{$field}; 531 532 return; 533 } 534 535 sub meta_pkg { 536 my $class = shift; 537 my $props = $class->properties; 538 return unless $props->{meta}; # this only works for meta-enabled classes 539 540 return $props->{meta_pkg} if $props->{meta_pkg}; 541 542 my $meta = ref $class || $class; 543 $meta .= '::Meta'; 544 return $props->{meta_pkg} = $meta; 545 } 546 547 sub has_column { 548 my $obj = shift; 549 return 1 if $obj->SUPER::has_column(@_); 550 return 1 if $obj->is_meta_column(@_); 551 return; 552 } 553 554 sub post_save_save_metadata { 555 my $obj = shift; 556 if (defined $obj && exists $obj->{__meta}) { 557 $obj->{__meta}->set_primary_keys($obj); 558 $obj->{__meta}->save; 559 } 560 } 561 562 sub meta { 563 my $obj = shift; 564 my ($name, $value) = @_; 565 566 return !$obj->{__meta} ? undef 567 : 2 == scalar @_ ? $obj->{__meta}->set($name, $value) 568 : 1 == scalar @_ ? $obj->{__meta}->get($name) 569 : $obj->{__meta}->get_hash 570 ; 571 } 572 573 sub meta_obj { 574 my $obj = shift; 575 return $obj->{__meta}; 576 } 577 578 sub column_func { 579 my $obj = shift; 580 my ($col) = @_; 581 return if !$col; 582 583 return $obj->SUPER::column_func(@_) 584 if !$obj->is_meta_column($col); 585 586 return sub { 587 my $obj = shift; 443 588 if (@_) { 444 $meta->{$var} = shift if @_; 445 $obj->{changed_cols}{$meta_col}++; 446 } 447 return $meta->{$var}; 448 } else { 449 my $meta = $obj->column($meta_col); 450 if (!ref $meta) { 451 $meta = __thaw_meta(\$meta); 452 $obj->{column_values}{$meta_col} = $meta; 453 } 454 # we should assume changes are going to be made, since 455 # we can't really monitor the hash once it has left us 456 $obj->{changed_cols}{$meta_col}++; 457 return $meta; 458 } 589 $obj->{__meta}->set($col, @_); 590 } 591 else { 592 $obj->{__meta}->get($col); 593 } 594 }; 459 595 } 460 596 … … 466 602 my $ret = sprintf '%04d-%02d-%02d %02d:%02d:%02d', unpack 'A4A2A2A2A2A2', $_[0]; 467 603 return $ret; 468 } 604 } 469 605 470 606 sub db2ts { … … 754 890 } 755 891 892 sub clone_all { 893 my $obj = shift; 894 my $clone = $obj->SUPER::clone_all(); 895 $clone->{__meta} = $obj->{__meta}; # TODO: clone this too 896 return $clone; 897 } 898 756 899 sub clone { 757 900 my $obj = shift; 758 901 my($param) = @_; 759 my $clone = $obj-> SUPER::clone_all;902 my $clone = $obj->clone_all(); 760 903 761 904 ## If the caller has listed a set of columns not to copy to the clone, … … 821 964 } 822 965 966 sub remove_meta { 967 my $obj = shift; 968 return 1 unless ref $obj; 969 my $mpkg = $obj->meta_pkg or return; 970 my $id_field = $obj->datasource . '_id'; 971 return $mpkg->remove({ $id_field => $obj->id }); 972 } 973 823 974 sub remove_children { 824 975 my $obj = shift; … … 834 985 my $obj_id = $obj->id; 835 986 for my $class (@classes) { 836 eval " use $class;";987 eval "# line " . __LINE__ . " " . __FILE__ . "\nuse $class;"; 837 988 $class->remove({ $key => $obj_id }); 838 989 } … … 943 1094 $def{key} = 1 if ($props->{primary_key}) && ($props->{primary_key} eq $col); 944 1095 $def{auto} = 1 if $def =~ m/\bauto[_ ]increment\b/i; 945 $def{default} = $props->{defaults}{$col} if exists $props->{defaults}{$col}; 1096 $def{default} = $props->{defaults}{$col} 1097 if exists $props->{defaults}{$col}; 946 1098 \%def; 947 1099 } … … 1008 1160 $hash; 1009 1161 } 1162 1163 package MT::Object::Meta; 1164 1165 use base qw( Data::ObjectDriver::BaseObject ); 1166 1167 sub driver { $MT::Object::DRIVER ||= MT::ObjectDriverFactory->new } 1168 1169 sub install_properties { 1170 my $class = shift; 1171 my ($props) = @_; 1172 $props->{column_defs}->{$_} ||= 'string' 1173 for @{ $props->{columns} }; 1174 $class->SUPER::install_properties(@_); 1175 } 1176 1177 sub meta_pkg { undef } 1178 1179 *table_name = \&MT::Object::table_name; 1180 *column_defs = \&MT::Object::column_defs; 1181 *column_def = \&MT::Object::column_def; 1182 *index_defs = \&MT::Object::index_defs; 1183 *__parse_defs = \&MT::Object::__parse_defs; 1184 *__parse_def = \&MT::Object::__parse_def; 1185 *count = \&MT::Object::count; 1186 *columns_of_type = \&MT::Object::columns_of_type; 1187 1188 # TODO: copy this too 1189 sub blob_requires_zip {} 1010 1190 1011 1191 1; -
branches/release-35/lib/MT/ObjectDriver/DDL/mysql.pm
r1174 r1927 22 22 my $field_prefix = $class->datasource; 23 23 my $table_name = $class->table_name; 24 local $dbh->{RaiseError} = 0; 24 25 my $sth = $dbh->prepare('SHOW INDEX FROM ' . $table_name) 25 26 or return undef; -
branches/release-35/lib/MT/Template.pm
r1877 r1927 35 35 'build_type' => 'smallint', 36 36 'build_interval' => 'integer', 37 38 # meta properties 39 'last_rebuild_time' => 'integer meta', 40 'page_layout' => 'string meta', 41 'include_with_ssi' => 'integer meta', 42 'use_cache' => 'integer meta', 43 'cache_expire_type' => 'integer meta', 44 'cache_expire_interval' => 'integer meta', 45 'cache_expire_event' => 'string meta', 37 46 }, 38 47 indexes => { … … 54 63 datasource => 'template', 55 64 primary_key => 'id', 56 });57 __PACKAGE__->install_meta({58 columns => [59 'last_rebuild_time',60 'page_layout',61 'include_with_ssi',62 'use_cache',63 'cache_expire_type',64 'cache_expire_interval',65 'cache_expire_event',66 ],67 65 }); 68 66 __PACKAGE__->add_trigger('pre_remove' => \&pre_remove_children); -
branches/release-35/lib/MT/Upgrade.pm
r1897 r1927 1352 1352 } 1353 1353 } 1354 1355 # handle schema updates for meta table 1356 if ($class->meta_pkg) { 1357 $self->check_type($type . ':meta'); 1358 } 1359 1354 1360 1; 1355 1361 } -
branches/release-35/t/09-image.t
r1100 r1927 25 25 ); 26 26 @drivers = qw( ImageMagick NetPBM ); 27 plan tests => scalar @Img + scalar @Img * scalar @drivers * 17; 27 plan tests => scalar @Img # file exists 28 + (scalar @Img * scalar @drivers * 18) # 18 tests each for every image and driver 29 ; 28 30 } 29 31 … … 38 40 39 41 for my $rec (@Img) { 40 my $img_file = File::Spec->catfile($BASE, 't', 'images', $rec->[0]); 42 my ($img_filename, $img_width, $img_height) = @$rec; 43 my $img_file = File::Spec->catfile($BASE, 't', 'images', $img_filename); 41 44 ok(-B $img_file, "$img_file looks like a binary file"); 42 45 … … 46 49 SKIP : { 47 50 skip("no $driver image", 17) unless $img; 48 isa_ok($img, 'MT::Image::' . $driver );51 isa_ok($img, 'MT::Image::' . $driver, "driver $driver with image $img_file is an MT::Image::$driver"); 49 52 # diag( MT::Image->errstr ) if MT::Image->errstr; 50 53 51 is($img->{width}, $rec->[1], "width is $rec->[1]");52 is($img->{height}, $ rec->[2], "height is $rec->[2]");53 my($w, $h) = $img->get_dimensions ;54 is($w, $ rec->[1], "width is $rec->[1]");55 is($h, $ rec->[2], "height is $rec->[2]");54 is($img->{width}, $img_width, "$driver says $img_filename is $img_width px wide"); 55 is($img->{height}, $img_height, "$driver says $img_filename is $img_height px high"); 56 my($w, $h) = $img->get_dimensions(); 57 is($w, $img_width, "${driver}'s get_dimensions says $img_filename is $img_width px wide"); 58 is($h, $img_height, "${driver}'s get_dimensions says $img_filename is $img_height px high"); 56 59 57 60 ($w, $h) = $img->get_dimensions(Scale => 50); 58 my($x, $y) = (int($img->{width} / 2), int($img->{height} / 2)); 59 is($w, $x, "width is $x"); 60 is($h, $y, "height is $y"); 61 my($x, $y) = (int($img_width / 2), int($img_height / 2)); 62 is($w, $x, "$driver says $img_filename at 50\% scale is $x px wide"); 63 is($h, $y, "$driver says $img_filename at 50\% scale is $y px high"); 64 65 ($w, $h) = $img->get_dimensions(); 66 is($w, $img_width, "${driver}'s get_dimensions says $img_filename is still $img_width px wide after theoretical scaling"); 67 is($h, $img_height, "${driver}'s get_dimensions says $img_filename is still $img_height px high after theoretical scaling"); 61 68 62 69 ($w, $h) = $img->get_dimensions(Width => 50); 63 is($w, 50, 'width is 50');70 is($w, 50, "$driver says $img_filename scaled to 50 px wide is 50 px wide"); 64 71 65 72 ($w, $h) = $img->get_dimensions(Width => 50, Height => 100); 66 is($w, 50, 'width is 50');67 is($h, 100, 'height is 100');73 is($w, 50, "$driver says $img_filename scaled to 50x100 is 50 px wide"); 74 is($h, 100, "$driver says $img_filename scaled to 50x100 is 100 px high"); 68 75 69 76 (my($blob), $w, $h) = $img->scale(Scale => 50); 70 ($x, $y) = (int($img ->{width} / 2), int($img->{height}/ 2));71 is($w, $x, " width is $x");72 is($h, $y, " height is $y");77 ($x, $y) = (int($img_width / 2), int($img_height / 2)); 78 is($w, $x, "result of scaling $img_filename to 50\% with $driver is $x px wide"); 79 is($h, $y, "result of scaling $img_filename to 50\% with $driver is $y px high"); 73 80 74 81 open FH, $img_file or die $!; … … 80 87 isa_ok($img, 'MT::Image::' . $driver); 81 88 # diag( MT::Image->errstr ) if MT::Image->errstr; 82 is($img->{width}, $rec->[1], "width is $rec->[1]");83 is($img->{height}, $ rec->[2], "height is $rec->[2]");89 is($img->{width}, $img_width, "$driver says $img_filename from blob is $img_width px wide"); 90 is($img->{height}, $img_height, "$driver says $img_filename from blob is $img_height px high"); 84 91 ($w, $h) = $img->get_dimensions; 85 is($w, $ rec->[1], "width is $rec->[1]");86 is($h, $ rec->[2], "height is $rec->[2]");92 is($w, $img_width, "${driver}'s get_dimensions says $img_filename from blob is $img_width px wide"); 93 is($h, $img_height, "${driver}'s get_dimensions says $img_filename from blob is $img_height px high"); 87 94 } # END SKIP 88 95 } -
branches/release-35/t/20-setup.t
r1458 r1927 39 39 ObjectDriver DBI::sqlite 40 40 PluginPath ../plugins 41 PluginPath plugins 41 42 CFG 42 43 close $fh; -
branches/release-35/t/22-author.t
r1100 r1927 8 8 use lib 'extlib'; 9 9 10 use Test::More tests => 54;10 use Test::More tests => 64; 11 11 12 12 use MT; … … 34 34 my $perm = $author->blog_perm(1); 35 35 ok($perm, "$author->blog_perm(1)") || die; 36 ok($author->can_edit_entry(1), ' can_edit_entry(1)');37 ok($author->can_edit_entry(2), ' can_edit_entry(2)');36 ok($author->can_edit_entry(1), 'Chuck D can edit entry #1'); 37 ok($author->can_edit_entry(2), 'Chuck D can edit entry #2'); 38 38 ok($perm->can_comment, 'can_comment'); 39 39 ok($perm->can_post, 'can_post'); … … 58 58 59 59 { 60 diag('meta field tests'); 61 62 my $author = MT::Author->load({ name => 'Chuck D' }); 63 ok(eval { $author->widgets(); 1 }, 'Author obj has widgets accessor'); 64 ok(!defined $author->widgets, "Author's widgets are undefined by default"); 65 ok(!defined $author->favorite_blogs, "Author's favorite blogs are undefined by default"); 66 67 # same as in MT::CMS::Dashboard, but that's not necessary 68 my $default_widgets = { 69 'blog_stats' => 70 { param => { tab => 'entry' }, order => 1, set => 'main' }, 71 'this_is_you-1' => { order => 1, set => 'sidebar' }, 72 'mt_shortcuts' => { order => 2, set => 'sidebar' }, 73 'mt_news' => { order => 3, set => 'sidebar' }, 74 }; 75 76 my $fav_blogs = [1, 7]; # not actually a blog #7, but meh 77 78 ok($author->widgets($default_widgets), "Author's widgets can be set"); 79 ok($author->favorite_blogs($fav_blogs), "Author's favorite blogs can be set"); 80 ok($author->save(), "Author with modified widgets can be saved"); 81 82 require MT::ObjectDriver::Driver::Cache::RAM; 83 MT::ObjectDriver::Driver::Cache::RAM->clear_cache(); 84 85 $author = MT::Author->load({ name => 'Chuck D' }); 86 87 ok($author->widgets, "Modified author has widgets"); 88 is_deeply($author->widgets, $default_widgets, "Author's widgets survived being saved accurately"); 89 90 ok($author->favorite_blogs, "Modified author has favorite blogs"); 91 is_deeply($author->favorite_blogs, $fav_blogs, "Author's favorite blogs survived being saved accurately"); 92 } 93 94 { 60 95 my $author = MT::Author->load({ name => 'Bob D' }); 61 96 $author = MT::Author->load($author->id); # silly ruse to force caching.... … … 68 103 ok( ! $author->can_view_log, 'can_view_log' ); 69 104 ok( ! $author->can_manage_plugins, 'can manage plugins'); 70 ok( ! $author->can_edit_entry(1), ' can_edit_entry(1)' );71 ok( $author->can_edit_entry(2), ' can_edit_entry(2)' );105 ok( ! $author->can_edit_entry(1), 'Bob D can edit entry #1' ); 106 ok( $author->can_edit_entry(2), 'Bob D can edit entry #2' ); 72 107 ok( $perm->can_post, 'can_post' ); 73 108 ok( $perm->can_create_post, 'can_create_post'); 74 109 ok( $perm->can_publish_post, 'can_publish_post'); 75 ok( ! $perm->can_upload, 'can_upload' );76 ok( ! $perm->can_edit_all_posts, ' can_edit_all_posts' );110 ok( $perm->can_upload, 'Bob D can *not* upload files' ); 111 ok( ! $perm->can_edit_all_posts, 'Bob D can *not* edit all posts' ); 77 112 ok( ! $perm->can_manage_pages, 'can_manage_pages'); 78 ok( $perm->can_edit_templates, 'can_edit_templates' );113 ok( ! $perm->can_edit_templates, 'Bob D can *not* edit templates' ); 79 114 ok( ! $perm->can_edit_tags, 'can_edit_tags'); 80 115 ok( ! $perm->can_edit_config, 'can_edit_config' ); 81 116 ok( ! $perm->can_set_publish_paths, 'can_set_publish_paths'); 82 117 ok( ! $perm->can_rebuild, 'can_rebuild' ); 83 ok( ! $perm->can_send_notifications, 'can_send_notifications' );118 ok( $perm->can_send_notifications, 'Bob D can send notifications' ); 84 119 ok( ! $perm->can_edit_categories, 'can_edit_categories' ); 85 120 ok( ! $perm->can_edit_notifications, 'can_edit_notifications' ); -
branches/release-35/t/64-objectmeta.t
r1548 r1927 2 2 3 3 use strict; 4 use lib ' extlib', 'lib';4 use lib 't/lib', 'extlib', 'lib'; 5 5 6 6 use Data::Dumper; 7 use Test::More tests => 13;7 use Test::More tests => 26; 8 8 9 9 use MT; 10 10 use MT::Object; 11 11 12 use vars qw( $DB_DIR $T_CFG ); 13 use MT::Test qw(:db); 14 12 15 my $mt = MT->instance; # plugins are go! 13 16 17 require MT::Awesome; 18 require MT::Awesome::Image; 14 19 15 package MT::Awesome; 20 my $file = MT::Awesome->new; 21 my $image = MT::Awesome::Image->new; 16 22 17 our @ISA = qw( MT::Object);23 #ok($file->has_column('meta'), 'having meta auto-adds meta column'); 18 24 19 __PACKAGE__->install_properties({ 20 column_defs => { 21 id => 'integer not null auto_increment', 22 title => 'string(255)', 23 file => 'string(255)', 24 }, 25 meta => 1, 26 class_type => 'foo', 27 }); 28 __PACKAGE__->install_meta({ 29 columns => [ 'mime_type' ] 30 }); 25 ok($file->is_meta_column('mime_type'), 'adding mime_type metadata field reports mime_type as a meta column'); 26 ok($file->has_column('mime_type'), 'adding mime_type metadata field reports mime_type as a column'); 27 ok(!defined $file->meta('mime_type'), 'unset metadata field is undefined'); 31 28 32 package MT::Awesome::Image;33 34 our @ISA = qw( MT::Awesome );35 36 __PACKAGE__->install_properties({37 class_type => 'image',38 });39 __PACKAGE__->install_meta({40 columns => [ 'width', 'height' ]41 });42 43 package main;44 45 my $file = new MT::Awesome;46 my $image = new MT::Awesome::Image;47 48 ok($file->has_column('meta'), 'having meta auto-adds meta column');49 ok(!defined $file->meta('mime_type'), 'unset metadata field is undefined');50 29 ok($file->meta('mime_type', 'archive/zip'), 'metadata field could be set'); 51 30 is($file->meta('mime_type'), 'archive/zip', 'new metadata value could be retrieved'); 52 ok($file->{changed_cols}{meta}, 'setting metadata field marked meta column as changed');53 31 is($file->mime_type, 'archive/zip', 'auto-installed metadata field method retrieved new value'); 32 33 diag('saving object'); 34 ok($file->save(), 'object with metadata could be saved'); 35 diag('object saved'); 36 ok($file->id, 'object with metadata received id when saved'); 37 is($file->meta('mime_type'), 'archive/zip', 'metadata value is still set after save'); 38 39 my $file_2 = MT::Awesome->load($file->id) 40 or diag('ERROR: ' . MT::Awesome->errstr); 41 ok($file_2, 'object with metadata could be loaded'); 42 is($file_2->meta('mime_type'), 'archive/zip', 'metadata value is correct on loaded object'); 43 44 is($file_2->mime_type, 'archive/zip', 'metadata value as retrieved with auto-installed method is correct on loaded object'); 45 46 #ok($file->{changed_cols}{meta}, 'setting metadata field marked meta column as changed'); 54 47 ok(!$file->has_meta('width'), 'metadata field on subclass did not install on superclass'); 55 48 … … 57 50 ok($image->width(300), 'metadata field on subclass could be set with auto-installed method'); 58 51 is($image->width, 300, 'auto-installed metadata field method retrieved new value for subclass'); 59 ok($image->{changed_cols}{meta}, 'setting metadata field on subclass with auto-installed method marked meta column as changed');52 #ok($image->{changed_cols}{meta}, 'setting metadata field on subclass with auto-installed method marked meta column as changed'); 60 53 ok($image->has_meta('width'), 'subclass has metadata field that was declared for subclass'); 61 54 ok($image->has_meta('mime_type'), 'subclass has metadata field that was declared for superclass'); 55 ok($image->mime_type('image/jpeg'), 'subclass object mime type set'); 56 ok($image->save(), 'image object saved'); 62 57 58 ok($image->id, 'image object with metadata received id when saved'); 59 60 my $image_2 = MT::Awesome->load($image->id); 61 ok($image_2, 'subclass object could be loaded'); 62 is($image_2->mime_type, 'image/jpeg', 'metadata value as retrieved with auto-installed method is correct on loaded image object'); 63 64 ok(MT::Asset::Image->has_meta('image_width'), 'MT::Asset::Image has an image_width meta column.'); 65 ok(MT::Entry->has_meta, 'MT::Entry has a meta support.'); 66 ok(MT::Page->has_meta, 'MT::Page has a meta support.'); 67 -
branches/release-35/t/lib/MT/Test.pm
r1652 r1927 6 6 use File::Spec; 7 7 use MT; 8 9 use Test::More; 8 10 9 11 BEGIN { … … 34 36 my $pkg = shift; 35 37 foreach my $opt (@_) { 36 $pkg->init_db() if $opt eq ':db'; 37 $pkg->init_test_db() if $opt eq ':testdb'; 38 $pkg->sample_data() if $opt eq ':data'; 38 if ($opt eq ':db') { 39 diag "Initializing database"; 40 $pkg->init_db(); 41 } 42 elsif ($opt eq ':testdb') { 43 $pkg->init_test_db(); 44 } 45 elsif ($opt eq ':data') { 46 diag "Initializing sample data"; 47 $pkg->sample_data(); 48 } 39 49 } 40 50 } … … 74 84 75 85 my $types = MT->registry('object_types'); 76 my @classes = map { $types->{$_} } grep { $_ !~ /\./ } keys %$types; 86 $types->{$_} = MT->model($_) for 87 grep { MT->model($_) } 88 map { $_ . ':meta' } 89 grep { MT->model($_)->meta_pkg } 90 sort keys %$types; 91 my @classes = map { $types->{$_} } grep { $_ !~ /\./ } sort keys %$types; 77 92 foreach my $class (@classes) { 78 93 if (ref($class) eq 'ARRAY') { 79 94 next; #TODO for now - it won't hurt when we do driver-tests. 80 95 } 81 els e{82 eval ' require '.$class or die $@;96 elsif (!defined *{ $class . '::__properties' }) { 97 eval '# line ' . __LINE__ . ' ' . __FILE__ . "\n" . 'require '.$class or die $@; 83 98 } 84 99 } … … 109 124 ); 110 125 eval { 126 # line __LINE__ __FILE__ 111 127 MT::Entry->remove; 112 128 MT::Comment->remove; … … 151 167 $blog->commenter_authenticators('enabled_TypeKey'); 152 168 $blog->save() or die "Couldn't save blog 1: ". $blog->errstr; 169 diag "Saved blog"; 153 170 154 171 require MT::Entry; … … 169 186 $chuckd->save() 170 187 or die "Couldn't save author record 2: " . $chuckd->errstr; 188 diag "Saved user Chuck D"; 171 189 172 190 my $bobd = MT::Author->new(); … … 181 199 $bobd->id(3); 182 200 $bobd->save() or die "Couldn't save author record 3: " . $bobd->errstr; 201 diag "Saved user Bob D"; 183 202 184 203 my $johnd = MT::Author->new(); … … 193 212 $johnd->id(4); 194 213 $johnd->save() or die "Couldn't save author record 4: " . $johnd->errstr; 214 diag "Saved user John Doe"; 195 215 196 216 my $hiro = MT::Author->new(); … … 206 226 $hiro->status(2); 207 227 $hiro->save() or die "Couldn't save author record 5: " . $hiro->errstr; 228 diag "Saved user Hiro"; 229 230 require MT::Role; 231 my ($admin_role, $author_role) = map { MT::Role->load({ name => $_ }) } 232 ('Blog Administrator', 'Author'); 208 233 209 234 require MT::Association; … … 211 236 $assoc->author_id($chuckd->id); 212 237 $assoc->blog_id(1); 213 $assoc->role_id( 3);238 $assoc->role_id($admin_role->id); 214 239 $assoc->type(1); 215 240 $assoc->save(); … … 218 243 $assoc->author_id($bobd->id); 219 244 $assoc->blog_id(1); 220 $assoc->role_id( 4);245 $assoc->role_id($author_role->id); 221 246 $assoc->type(1); 222 247 $assoc->save(); … … 225 250 $assoc->author_id($hiro->id); 226 251 $assoc->blog_id(1); 227 $assoc->role_id( 3);252 $assoc->role_id($admin_role->id); 228 253 $assoc->type(1); 229 254 $assoc->save(); … … 255 280 $entry->tags('rain', 'grandpa', 'strolling'); 256 281 $entry->save() or die "Couldn't save entry record 1: ".$entry->errstr; 282 diag "Saved entry #1"; 257 283 } 258 284 … … 275 301 $entry->id(2); 276 302 $entry->save() or die "Couldn't save entry record 2: ".$entry->errstr; 303 diag "Saved entry #2"; 277 304 } 278 305 … … 297 324 $entry->tags('anemones'); 298 325 $entry->save() or die "Couldn't save entry record 3: ".$entry->errstr; 326 diag "Saved entry #3"; 299 327 } 300 328 … … 327 355 $ping->visible(1); 328 356 $ping->save or die "Couldn't save TBPing record 1: " . $ping->errstr; 357 diag "Saved a trackback ping"; 329 358 } 330 359 … … 355 384 $cat->id(1); 356 385 $cat->save or die "Couldn't save category record 1: ". $cat->errstr; 386 diag "Saved category foo"; 357 387 } 358 388 … … 367 397 $cat->id(2); 368 398 $cat->save or die "Couldn't save category record 2: ". $cat->errstr; 399 diag "Saved category bar"; 369 400 } 370 401 … … 379 410 $tb->id(2); 380 411 $tb->save or die "Couldn't save Trackback record 2: " . $tb->errstr;; 412 diag "Saved category bar's trackback target"; 381 413 } 382 414 … … 391 423 $cat->id(3); 392 424 $cat->save or die "Couldn't save category record 3: ". $cat->errstr; 425 diag "Saved subcategory subfoo"; 393 426 } 394 427 … … 427 460 $place->save 428 461 or die "Couldn't save placement record: ".$place->errstr; 462 diag "Placed entry Verse 3 in category foo"; 429 463 } 430 464 if ($i == 4) { … … 436 470 $place->save 437 471 or die "Couldn't save placement record: ".$place->errstr; 472 diag "Placed entry Verse 4 in category subfoo"; 438 473 } 439 474 } … … 458 493 $cmt->id(1); 459 494 $cmt->save() or die "Couldn't save comment record 1: ".$cmt->errstr; 495 diag "Saved comment #1"; 460 496 461 497 $cmt->id(11); … … 465 501 $cmt->parent_id(1); 466 502 $cmt->save() or die "Couldn't save comment record 11: ".$cmt->errstr; 503 diag "Saved child comment #11"; 467 504 468 505 $cmt->id(12); … … 472 509 $cmt->parent_id(11); 473 510 $cmt->save() or die "Couldn't save comment record 12: ".$cmt->errstr; 511 diag "Saved child comment #12"; 474 512 } 475 513 # entry id 5 - 1 comment, commenting is off (closed) … … 587 625 $tmpl->type('custom'); 588 626 $tmpl->save or die "Couldn't save template record 1: ".$tmpl->errstr; 627 diag "Saved blog-name template"; 589 628 590 629 ### Asset … … 600 639 $asset->file_ext('jpg'); 601 640 $asset->image_width(640); 641 diag "Set image asset's width metadata field"; 602 642 $asset->image_height(480); 603 643 $asset->mime_type('image/jpeg'); … … 606 646 $asset->created_by(1); 607 647 $asset->tags('alpha', 'beta', 'gamma'); 608 $asset->save; 648 diag "Tagged image asset"; 649 $asset->save or die "Couldn't save asset record 1: " . $asset->errstr; 650 diag "Saved image asset"; 609 651 610 652 $asset->set_score('unit test', $bobd, 5, 1); … … 624 666 $asset->created_on('19780131074500'); 625 667 $asset->tags('beta'); 626 $asset->save; 668 $asset->save or die "Couldn't save file asset record: " . $asset->errstr; 669 diag "Saved file asset"; 627 670 628 671 $asset->set_score('unit test', $chuckd, 2, 1); … … 642 685 $e4->set_score('unit test', $chuckd, 2, 1); 643 686 $e4->set_score('unit test', $johnd, 3, 1); 687 diag "Saved scores for entry #4"; 644 688 645 689 ## Page … … 672 716 $folder->id(20); 673 717 $folder->save or die "Could'n sae folder record 20:" . $folder->errstr; 718 diag "Saved folder #20"; 674 719 675 720 $folder = MT::Folder->new();
