| 880 | | } |
| | 880 | 'core_upgrade_meta' => { |
| | 881 | version_limit => 4.0057, |
| | 882 | priority => 3.2, |
| | 883 | code => \&core_upgrade_meta, |
| | 884 | }, |
| | 885 | |
| | 886 | # Helper upgrade routine for core_upgrade_meta |
| | 887 | # and possibly other object types that require |
| | 888 | # this migration; version_limit is unspecified, so |
| | 889 | # this can only be invoked if another upgrade |
| | 890 | # operation utilizes it. |
| | 891 | 'core_upgrade_meta_for_table' => { |
| | 892 | priority => 3.3, |
| | 893 | code => \&core_upgrade_meta_for_table, |
| | 894 | }, |
| | 895 | }; |
| | 896 | } |
| | 897 | |
| | 898 | sub core_upgrade_meta { |
| | 899 | my $self = shift; |
| | 900 | # we could possibly determine the list of types to process |
| | 901 | # programmatically, but this will do... |
| | 902 | $self->add_step('core_upgrade_meta_for_table', type => 'entry'); |
| | 903 | $self->add_step('core_upgrade_meta_for_table', type => 'author'); |
| | 904 | $self->add_step('core_upgrade_meta_for_table', type => 'blog'); |
| | 905 | $self->add_step('core_upgrade_meta_for_table', type => 'template'); |
| | 906 | $self->add_step('core_upgrade_meta_for_table', type => 'asset'); |
| | 907 | $self->add_step('core_upgrade_meta_for_table', type => 'category', |
| | 908 | plugindata => 1); |
| | 909 | return 0; |
| | 910 | } |
| | 911 | |
| | 912 | sub core_upgrade_meta_for_table { |
| | 913 | my $self = shift; |
| | 914 | my (%param) = @_; |
| | 915 | my $type = $param{type}; |
| | 916 | return 0 unless $type; |
| | 917 | my $class = MT->model($type); |
| | 918 | return 0 unless $class; |
| | 919 | my $cfclass = MT->model('field'); |
| | 920 | my $plugindata = $param{plugindata} || 0; |
| | 921 | |
| | 922 | if ($cfclass) { |
| | 923 | # this looks weird, but it winds up invoking |
| | 924 | # the loading of custom field types and the |
| | 925 | # installation of their meta properties |
| | 926 | MT->registry('tags'); |
| | 927 | } |
| | 928 | |
| | 929 | # special case for types that use CustomField plugindata |
| | 930 | # for storing their custom field metadata instead of a 'meta' |
| | 931 | # column. |
| | 932 | if ($cfclass && $plugindata) { |
| | 933 | require CustomFields::Upgrade; |
| | 934 | $self->progress($self->translate_escape('Moving metadata storage for categories...')); |
| | 935 | CustomFields::Upgrade::customfields_move_meta($self, $type); |
| | 936 | return 0; |
| | 937 | } |
| | 938 | |
| | 939 | my $offset = int($param{offset} || 0); |
| | 940 | my $count = int($param{count} || 0); |
| | 941 | |
| | 942 | my $pid = $param{step} . "_type"; |
| | 943 | |
| | 944 | my $msg = MT->translate("Upgrading metadata storage for [_1]", $class->class_label_plural); |
| | 945 | |
| | 946 | if (!$offset) { |
| | 947 | $self->progress($msg, $pid); |
| | 948 | } else { |
| | 949 | my $count = $class->count(); |
| | 950 | return 0 unless $count; |
| | 951 | $self->progress(sprintf($msg . " (%d%%)", ($offset/$count*100)), $pid); |
| | 952 | } |
| | 953 | |
| | 954 | my $driver = $class->dbi_driver; |
| | 955 | my $dbh = $driver->rw_handle; |
| | 956 | my $dbd = $driver->dbd; |
| | 957 | my $stmt = $dbd->sql_class->new; |
| | 958 | |
| | 959 | # assumes 'meta' is the meta column name; should be for all core types |
| | 960 | # we are processing |
| | 961 | my $meta_col = $dbd->db_column_name($class->datasource, |
| | 962 | $param{meta_column} || 'meta'); |
| | 963 | my $id_col = $dbd->db_column_name($class->datasource, 'id'); |
| | 964 | $stmt->add_where( $meta_col => { not_null => 1 } ); |
| | 965 | $stmt->limit( 101 ); |
| | 966 | $stmt->offset( $offset ) if $offset; |
| | 967 | |
| | 968 | my $sql = join ' ', 'SELECT', $meta_col, ',', $id_col, 'FROM', |
| | 969 | $driver->table_for($class), |
| | 970 | $stmt->as_sql_where(), |
| | 971 | $stmt->as_limit; |
| | 972 | |
| | 973 | my $sth = $dbh->prepare($sql) |
| | 974 | or return $self->error($dbh->errstr || $DBI::errstr); |
| | 975 | $sth->execute |
| | 976 | or return $self->error($dbh->errstr || $DBI::errstr); |
| | 977 | |
| | 978 | my $rows = 0; |
| | 979 | |
| | 980 | require MT::Serialize; |
| | 981 | my $ser = MT::Serialize->new('MT'); |
| | 982 | my %fields; |
| | 983 | |
| | 984 | my @ids; |
| | 985 | while (my $row = $sth->fetchrow_arrayref) { |
| | 986 | $rows++; |
| | 987 | my ($rawmeta, $id) = @$row; |
| | 988 | if ($rawmeta =~ m/^SERG/) { |
| | 989 | # deserialize |
| | 990 | my $metadataref = $ser->unserialize($rawmeta); |
| | 991 | if ($metadataref) { |
| | 992 | my $metadata = $$metadataref; |
| | 993 | my $obj = $class->load($id); |
| | 994 | if ($obj) { |
| | 995 | my $changed = 0; |
| | 996 | foreach my $metaname (keys %$metadata) { |
| | 997 | my $metavalue = $metadata->{$metaname}; |
| | 998 | if ($metaname eq 'customfields') { |
| | 999 | next unless $cfclass; |
| | 1000 | |
| | 1001 | # extra work for custom fields; a hash into itself |
| | 1002 | my $cfdata = $metavalue; |
| | 1003 | next unless ref $cfdata eq 'HASH'; |
| | 1004 | |
| | 1005 | foreach my $cfname (keys %$cfdata) { |
| | 1006 | my $cfvalue = $cfdata->{$cfname}; |
| | 1007 | # make sure CustomFields::Field exists |
| | 1008 | my $fld = $fields{$cfname} ||= $cfclass->load({ basename => $cfname, obj_type => $type }); |
| | 1009 | next unless $fld; |
| | 1010 | |
| | 1011 | $changed++; |
| | 1012 | $obj->meta('field.' . $cfname, $cfvalue); |
| | 1013 | } |
| | 1014 | } else { |
| | 1015 | $changed++; |
| | 1016 | $obj->meta($metaname, $metavalue); |
| | 1017 | } |
| | 1018 | } |
| | 1019 | if ($changed) { |
| | 1020 | $obj->save if $changed; |
| | 1021 | push @ids, $obj->id; |
| | 1022 | } |
| | 1023 | } |
| | 1024 | } |
| | 1025 | } |
| | 1026 | last if $rows == 100; |
| | 1027 | } |
| | 1028 | $sth->finish; |
| | 1029 | |
| | 1030 | # now, clear the meta column for each of the objects touched |
| | 1031 | if (@ids) { |
| | 1032 | my $list = join ",", @ids; |
| | 1033 | my $sql = join " ", "UPDATE", $driver->table_for($class), |
| | 1034 | "SET", $meta_col, "=NULL WHERE", $id_col, " IN ($list)"; |
| | 1035 | $dbh->do($sql); |
| | 1036 | } |
| | 1037 | |
| | 1038 | if ($rows == 101) { |
| | 1039 | $offset += 100; |
| | 1040 | } else { |
| | 1041 | # done, so lets drop that meta column, what say you? |
| | 1042 | $sql = $dbd->ddl_class->drop_column_sql($class, $param{meta_column} || 'meta'); |
| | 1043 | $dbh->do($sql); |
| | 1044 | $offset = 0; # done! |
| | 1045 | } |
| | 1046 | return $offset; |