June 12, 2004

Getting Postgres and Movable Type to play well together

There is a bug in Movable Type: only single values (i.e scalar references) can be stored with MT::PluginData when used with a PostgreSQL database. Complex values (i.e. non-scalar references) can be stored with MySQL however, because MySQL allows binary data in its text columns (I would consider that incorrect behavior myself). PostgreSQL does not. So, to fix the problem, the binary data that the Storable perl module outputs needs to be converted to text before reaching the database. To do this, I made use of pack and unpack to trasform the data to and from hexadecimal before it gets to the database itself. I also needed to be sure to preserve any existing data already stored in the database.

The updated data method of MT::PluginData looks like this:

sub data {
    my $data = shift;
    $data->column('data', unpack ("H*", freeze(shift))) if @_;
    my $r;
    eval {
        $r = thaw($data->column('data'));
    };
    $r = thaw(pack ("H*", $data->column('data'))) if ($@);
    $r;
}

Posted by rayners | Comments (3) | TrackBack

September 11, 2003

Updating MT to play well with Atom

While cobbling together an Atom implementation for MovableType, I've had to change how passwords are stored. After a quick email from Mark, I changed lib/MT/Author.pm slightly to make the passwords stored in a manner more friendly to the current Atom Authentication Scheme, and allow easy migration of current passwords.

Here is the diff:

--- Author.pm.old       Thu May 29 00:04:58 2003
+++ Author.pm   Thu Sep 11 09:13:48 2003
@@ -6,6 +6,8 @@
 package MT::Author;
 use strict;

+use Digest::SHA1 qw( sha1_hex );
+
 use MT::Object;
 @MT::Author::ISA = qw( MT::Object );
 __PACKAGE__->install_properties({
@@ -25,9 +27,8 @@
 sub set_password {
     my $auth = shift;
     my($pass) = @_;
-    my @alpha = ('a'..'z', 'A'..'Z', 0..9);
-    my $salt = join '', map $alpha[rand @alpha], 1..2;
-    $auth->column('password', crypt $pass, $salt);
+    $auth->column ('password',
+       sha1_hex (join (':', $auth->column ('name'), 'MovableType', $pass)));
 }

 sub is_valid_password {
@@ -35,8 +36,15 @@
     my($pass, $crypted) = @_;
     $pass ||= '';
     my $real_pass = $auth->column('password');
-    return $crypted ? $real_pass eq $pass :
-                      crypt($pass, $real_pass) eq $real_pass;
+    return 1 if ($crypted ? $pass eq $real_pass :
+       sha1_hex (join (':', $auth->column ('name'), 'MovableType', $pass))
+       eq $real_pass);
+    if (crypt ($pass, $real_pass) eq $real_pass) {
+      $auth->set_password ($pass);
+      return 1;
+    }
+
+    return 0;
 }

 sub remove {

Posted by rayners | Comments (0) | TrackBack

August 20, 2003

Perl version of the Comment Queue hack

You have probably all seen the Comment Queue hack over at scriptygoddess. Well, a couple nights ago Cheyenne asked me to install it for her, as she has been having troll problems lately.

The Comment Queue script requires PHP and a MySQL backend and gnome-girl.com is not using a MySQL backend. Needless to say, it would be a chore and then some to migrate her site to MySQL. So, as you may have heard if you read her site, I quickly threw together a perl version for her to use, and I thought I would release it to the general public now. The interface leaves a little (okay, quite a bit) to be desired, but it works.

So, if you want to use it, after you perform the changes to the MT files and database (only if you are using MySQL) as described in the hack, just download one of the following files, unpack it, and place mt-comments-pending.cgi in your base MT directory. Be sure to chmod it to 755 so it can be executed.

Posted by rayners | Comments (6) | TrackBack

November 27, 2002

Author Archives in Movable Type

Because of the way I wanted to use MT for one of my blogs, I thought Author-based archiving could be extremely useful. Instead, initially, I had to do a little work-around using Categories. But, today I sat down and implemented Author Archives in MT.

You can see this code in action on the campaign journal blog I setup for a game I was in.

The following is the diff I generated from my new code, which I will make available for anyone who is interested in testing:

diff -c -r ./lib/MT/App/CMS.pm /usr/local/www/data/mt/lib/MT/App/CMS.pm *** ./lib/MT/App/CMS.pm Wed Oct 30 15:36:37 2002 --- /usr/local/www/data/mt/lib/MT/App/CMS.pm Wed Nov 27 14:07:57 2002 *************** *** 517,522 **** --- 517,523 ---- $obj->type eq 'custom' || $obj->type eq 'archive' || $obj->type eq 'category' || + $obj->type eq 'author' || $obj->type eq 'individual'; $param{has_outfile} = $obj->type eq 'index'; $param{has_rebuild} = $obj->type eq 'index'; *************** *** 628,633 **** --- 629,635 ---- $q->param('type') eq 'custom' || $q->param('type') eq 'archive' || $q->param('type') eq 'category' || + $q->param('type') eq 'author' || $q->param('type') eq 'individual'; $param{has_outfile} = $q->param('type') eq 'index'; $param{has_rebuild} = $q->param('type') eq 'index'; *************** *** 875,881 **** $obj->convert_paras_comments(1); $obj->ping_weblogs(0); $obj->ping_blogs(0); ! $obj->archive_type('Individual,Monthly'); $obj->archive_type_preferred('Individual'); $obj->status_default(1); } --- 877,883 ---- $obj->convert_paras_comments(1); $obj->ping_weblogs(0); $obj->ping_blogs(0); ! $obj->archive_type('Individual,Monthly,Author'); $obj->archive_type_preferred('Individual'); $obj->status_default(1); } *************** *** 917,923 **** "Populating blog with default templates failed: [_1]", $tmpl->errstr)); if ($val->{type} eq 'archive' || $val->{type} eq 'category' || ! $val->{type} eq 'individual') { push @arch_tmpl, $tmpl; } } --- 919,925 ---- "Populating blog with default templates failed: [_1]", $tmpl->errstr)); if ($val->{type} eq 'archive' || $val->{type} eq 'category' || ! $val->{type} eq 'individual' || $val->{type} eq 'author') { push @arch_tmpl, $tmpl; } } *************** *** 931,937 **** @at = qw( Category ); } elsif ($tmpl->type eq 'individual') { @at = qw( Individual ); ! } require MT::TemplateMap; for my $at (@at) { my $map = MT::TemplateMap->new; --- 933,941 ---- @at = qw( Category ); } elsif ($tmpl->type eq 'individual') { @at = qw( Individual ); ! } elsif ($tmpl->type eq 'author') { ! @at = qw( Author ); ! } require MT::TemplateMap; for my $at (@at) { my $map = MT::TemplateMap->new; *************** *** 1025,1031 **** } elsif ($obj->type eq 'custom') { push @custom_data, $row; } elsif ($obj->type eq 'archive' || $obj->type eq 'category' || ! $obj->type eq 'individual') { push @archive_data, $row; } else { $param{'template_' . $obj->type} = $obj->id; --- 1029,1035 ---- } elsif ($obj->type eq 'custom') { push @custom_data, $row; } elsif ($obj->type eq 'archive' || $obj->type eq 'category' || ! $obj->type eq 'individual' || $obj->type eq 'author') { push @archive_data, $row; } else { $param{'template_' . $obj->type} = $obj->id; *************** *** 1857,1863 **** while (my $tmpl = $iter->()) { my $type = $tmpl->type; next unless $type eq 'archive' || $type eq 'category' || ! $type eq 'individual'; $tmpl_name{$tmpl->id} = $tmpl->name; } my %map; --- 1861,1867 ---- while (my $tmpl = $iter->()) { my $type = $tmpl->type; next unless $type eq 'archive' || $type eq 'category' || ! $type eq 'individual' || $type eq 'author'; $tmpl_name{$tmpl->id} = $tmpl->name; } my %map; *************** *** 1875,1881 **** $total_rows++; } my @data; ! for my $at (qw( Individual Daily Weekly Monthly Category )) { $map{$at} = [] unless $map{$at}; my @map = sort { $a->{map_template_name} cmp $b->{map_template_name} } @{ $map{$at} }; --- 1879,1885 ---- $total_rows++; } my @data; ! for my $at (qw( Individual Daily Weekly Monthly Category Author )) { $map{$at} = [] unless $map{$at}; my @map = sort { $a->{map_template_name} cmp $b->{map_template_name} } @{ $map{$at} }; *************** *** 1961,1967 **** while (my $tmpl = $iter->()) { my $type = $tmpl->type; next unless $type eq 'archive' || $type eq 'category' || ! $type eq 'individual'; push @tmpl, { template_id => $tmpl->id, template_name => $tmpl->name }; } @tmpl = sort { $a->{template_name} cmp $b->{template_name} } @tmpl; --- 1965,1971 ---- while (my $tmpl = $iter->()) { my $type = $tmpl->type; next unless $type eq 'archive' || $type eq 'category' || ! $type eq 'individual' || $type eq 'author'; push @tmpl, { template_id => $tmpl->id, template_name => $tmpl->name }; } @tmpl = sort { $a->{template_name} cmp $b->{template_name} } @tmpl; diff -c -r ./lib/MT/Entry.pm /usr/local/www/data/mt/lib/MT/Entry.pm *** ./lib/MT/Entry.pm Mon Oct 7 23:29:29 2002 --- /usr/local/www/data/mt/lib/MT/Entry.pm Wed Nov 27 13:33:57 2002 *************** *** 211,217 **** $at = $blog->archive_type_preferred || $blog->archive_type; return '' if !$at || $at eq 'None'; my %at = map { $_ => 1 } split /,/, $at; ! for my $tat (qw( Individual Daily Weekly Monthly Category )) { $at = $tat if $at{$tat}; } } --- 211,217 ---- $at = $blog->archive_type_preferred || $blog->archive_type; return '' if !$at || $at eq 'None'; my %at = map { $_ => 1 } split /,/, $at; ! for my $tat (qw( Individual Daily Weekly Monthly Category Author )) { $at = $tat if $at{$tat}; } } diff -c -r ./lib/MT/Template/Context.pm /usr/local/www/data/mt/lib/MT/Template/Context.pm *** ./lib/MT/Template/Context.pm Tue Oct 29 19:48:10 2002 --- /usr/local/www/data/mt/lib/MT/Template/Context.pm Wed Nov 27 13:47:38 2002 *************** *** 1261,1266 **** --- 1261,1268 ---- return '' unless @entries; if ($ctx->{current_archive_type} eq 'Category') { return $ctx->stash('archive_category')->label; + } elsif ($ctx->{current_archive_type} eq 'Author') { + return $ctx->stash('archive_author')->name; } else { my $st = $TypeHandlers{$ctx->{current_archive_type}}{section_title}; my $title = $st->($ctx, $entries[0]); diff -c -r ./lib/MT/Template.pm /usr/local/www/data/mt/lib/MT/Template.pm *** ./lib/MT/Template.pm Tue Oct 29 19:55:19 2002 --- /usr/local/www/data/mt/lib/MT/Template.pm Wed Nov 27 14:01:57 2002 *************** *** 12,17 **** --- 12,18 ---- archive => 'Archive', category => 'Category Archive', individual => 'Individual', + author => 'Author Archive', comments => 'Comment Listing', pings => 'Ping Listing', comment_preview => 'Comment Preview', diff -c -r ./lib/MT/Util.pm /usr/local/www/data/mt/lib/MT/Util.pm *** ./lib/MT/Util.pm Tue Oct 29 19:51:04 2002 --- /usr/local/www/data/mt/lib/MT/Util.pm Wed Nov 27 13:33:57 2002 *************** *** 395,400 **** --- 395,412 ---- } $file = sprintf("cat_%s", $label); } + } elsif ($at eq 'Author') { + require MT::Author; + my $this_author = MT::Author->load({ id => $entry->author_id }); + if ($file_tmpl) { + $ctx->stash('archive_author', $this_author); + } else { + my $label = ''; + if ($this_author) { + $label = dirify($this_author->name); + } + $file = sprintf("author_%s", $label); + } } else { return $entry->error(MT->translate( "Invalid Archive Type setting '[_1]'", $at )); diff -c -r ./lib/MT/default-templates.pl /usr/local/www/data/mt/lib/MT/default-templates.pl *** ./lib/MT/default-templates.pl Tue Oct 29 19:54:13 2002 --- /usr/local/www/data/mt/lib/MT/default-templates.pl Wed Nov 27 13:33:57 2002 *************** *** 996,1001 **** --- 996,1081 ---- <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> + <title><$MTBlogName$>: <$MTArchiveTitle$> Archives</title> + + <link rel="stylesheet" href="<$MTBlogURL$>styles-site.css" type="text/css" /> + <link rel="alternate" type="application/rss+xml" title="RSS" href="<$MTBlogURL$>index.rdf" /> + + <script language="javascript" type="text/javascript"> + function OpenComments (c) { + window.open(c, + \'comments\', + \'width=480,height=480,scrollbars=yes,status=yes\'); + } + + function OpenTrackback (c) { + window.open(c, + \'trackback\', + \'width=480,height=480,scrollbars=yes,status=yes\'); + } + </script> + + </head> + + <body> + + <div id="banner"> + <h1><a href="<$MTBlogURL$>" accesskey="1"><$MTBlogName$></a></h1> + <span class="description"><$MTBlogDescription$></span> + </div> + + <div id="container"> + + <div class="blog"> + <MTEntries> + <$MTEntryTrackbackData$> + + <MTDateHeader> + <h2 class="date"><$MTEntryDate format="%x"$></h2> + </MTDateHeader> + + <div class="blogbody"> + + <a name="<$MTEntryID pad="1"$>"></a> + <h3 class="title"<>$MTEntryTitle$></h3> + + <$MTEntryBody$> + + <MTEntryIfExtended> + <$MTEntryMore$> + </MTEntryIfExtended> + + <div class="posted"> + Posted by <$MTEntryAuthor$> at <a href="<$MTEntryPermalink$>"><$MTEntryDate format="%X"$></a> + <MTEntryIfAllowComments> + | <a href="<$MTCGIPath$><$MTCommentScript$>?entry_id=<$MTEntryID$>" onclick="OpenComments(this.href); return false">Comments (<$MTEntryCommentCount$>)</a> + </MTEntryIfAllowComments> + <MTEntryIfAllowPings> + | <a href="<$MTCGIPath$><$MTTrackbackScript$>?__mode=view&amp;entry_id=<$MTEntryID$>" onclick="OpenTrackback(this.href); return false">TrackBack</a> + </MTEntryIfAllowPings> + </div> + + </div> + + </MTEntries> + </div> + + </div> + + </body> + </html> + ', + 'type' => 'author', + 'name' => 'Author Archive' + }, + + { + 'text' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + + <html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> + <title><$MTBlogName$>: <$MTEntryTitle$></title> <link rel="stylesheet" href="<$MTBlogURL$>styles-site.css" type="text/css" /> diff -c -r ./lib/MT.pm /usr/local/www/data/mt/lib/MT.pm *** ./lib/MT.pm Thu Oct 31 03:51:59 2002 --- /usr/local/www/data/mt/lib/MT.pm Wed Nov 27 13:33:57 2002 *************** *** 341,347 **** --- 341,356 ---- { 'join' => [ 'MT::Placement', 'entry_id', { category_id => $cat->id } ] }); $ctx->stash('entries', \@entries); + } elsif ($at eq 'Author') { + my $author; + require MT::Author; + $author = MT::Author->load({ id => $entry->author_id }); + $ctx->stash('archive_author', $author); + my @entries = MT::Entry->load({ blog_id => $blog->id, + author_id => $author->id, status => MT::Entry::RELEASE() }); + $ctx->stash('entries', \@entries); } + my $fmgr = $blog->file_mgr; my $arch_root = $blog->archive_path; diff -c -r ./mt-load.cgi /usr/local/www/data/mt/mt-load.cgi *** ./mt-load.cgi Mon Sep 16 02:33:04 2002 --- /usr/local/www/data/mt/mt-load.cgi Wed Nov 27 13:36:37 2002 *************** *** 77,83 **** print " Loading blog...\n"; my $blog = MT::Blog->new; $blog->name('First Blog'); ! $blog->archive_type('Individual,Monthly'); $blog->archive_type_preferred('Individual'); $blog->days_on_index(7); $blog->words_in_excerpt(20); --- 77,83 ---- print " Loading blog...\n"; my $blog = MT::Blog->new; $blog->name('First Blog'); ! $blog->archive_type('Individual,Monthly,Author'); $blog->archive_type_preferred('Individual'); $blog->days_on_index(7); $blog->words_in_excerpt(20); *************** *** 122,128 **** $obj->blog_id($blog->id); $obj->save or die $obj->errstr; if ($val->{type} eq 'archive' || $val->{type} eq 'individual' || ! $val->{type} eq 'category') { push @arch_tmpl, $obj; } } --- 122,128 ---- $obj->blog_id($blog->id); $obj->save or die $obj->errstr; if ($val->{type} eq 'archive' || $val->{type} eq 'individual' || ! $val->{type} eq 'category' || $val->{type} eq 'author') { push @arch_tmpl, $obj; } } *************** *** 138,143 **** --- 138,145 ---- @at = qw( Category ); } elsif ($tmpl->type eq 'individual') { @at = qw( Individual ); + } elsif ($tmpl->type eq 'author') { + @at = qw( Author ); } for my $at (@at) { print " Mapping template ID '", $tmpl->id, "' to '$at'\n";

Posted by rayners | Comments (4) | TrackBack

Entry List