3 # -*- perl -*- vim: ft=perl sw=4 sts=4
10 use vars qw(%opts $mt);
12 if (-r $ENV{HOME} . "/.blogclrc") {
13 open CONFIG, $ENV{HOME} . "/.blogclrc";
16 next unless m/^([^=]*)=(.*)/;
22 Getopt::Long::Configure ("bundling", "auto_help");
23 die "Invalid arguments\n" unless
24 GetOptions (\%opts, 'username|l=s', 'password|p=s', 'url|u=s', 'blogid|b=i', 'title|t=s', 'category|c=s', 'file|f=s');
26 if ($ARGV[0] eq "help") {
27 Getopt::Long::HelpMessage();
32 die "No url given.\n" unless(defined($opts{url}));
33 die "No username given.\n" unless(defined($opts{username}));
34 die "No passowrd given.\n" unless(defined($opts{password}));
35 $mt = new Net::MovableType($opts{'url'}) || die "$!";
36 $mt->username($opts{'username'});
37 $mt->password($opts{'password'});
38 my $blogid = $opts{'blogid'} || $mt->resolveBlogId($opts{'username'});
40 my $userblogs=$mt->getUsersBlogs();
41 if($userblogs && $userblogs->[0]->{blogid}) {
42 $blogid=$userblogs->[0]->{'blogid'};
44 warn "Blog ID unknown.\n";
51 my $entries = $mt->getRecentPosts(shift || 10);
52 while ( my $entry = shift @$entries ) {
53 printf("[%02d] - %s\n",
54 $entry->{postid}, $entry->{title} );
56 # postid userid content title description dateCreated
59 sub print_categories {
60 print join("\n",map {$_->{categoryName}} @{$mt->getCategoryList()}), "\n";
65 my $post = $mt->getPost($postid) or die "$!";
66 my $categories = $mt->getPostCategories($postid) or die "$!";
67 if(defined($opts{file})) {
68 if($opts{file} eq "-") {
69 print STDOUT $post->{description}, "\n";
71 open FH, ">".$opts{file} or die "$!";
72 print $opts{file}, "\n";
73 print FH $post->{description}, "\n";
77 printf("Title: %s (%d)\n", $post->{title}, $post->{postid});
80 } elsif (@$categories==1) {
83 foreach(@$categories) {
84 print " ", $_->{'categoryName'};
86 print "\n" if(@$categories);
88 print $post->{description}, "\n";
94 if(defined($opts{file})) {
95 open FILE, $opts{file} or die "$!";
96 chop($string = join("", <FILE>));
100 my $editor = $ENV{VISUAL} || $ENV{EDITOR};
101 $editor ||= "/usr/bin/sensible-editor" if (-x "/usr/bin/sensible-editor");
102 $editor ||= "/bin/vi" if (-x "/bin/vi");
103 $editor ||= "/usr/bin/nano" if (-x "/usr/bin/nano");
104 my $tmp = new File::Temp(SUFFIX => '.html');
105 print $tmp $string, "\n";
107 my $mtime = (stat($tmp->filename))[9];
108 system(split (/ /, $editor), $tmp->filename);
109 if($mtime == (stat($tmp->filename))[9]) {
113 chop($string = join("", <$tmp>));
120 my $title = $opts{title} || Complete('Title: ');
121 my $category = $opts{category} || Complete('Categories: ', map {$_->{categoryName}} @{$mt->getCategoryList()});
122 die "No title given.\n" unless($title);
123 my $description = edit_string("");
124 if(defined($description)) {
125 my $postid = $mt->newPost({ 'title' => $title, 'description' => $description });
126 $mt->setPostCategories($postid, $category ? [split(/[, ]+/, $category)] : $category) || warn "$!"
127 if(defined($category));
128 $mt->publishPost($postid);
130 die "No change. Aborting.\n";
136 my $post = $mt->getPost($postid);
137 my $categories = $mt->getPostCategories($postid);
138 my $title = $opts{title} || Complete('Title: ', $post->{title}) || $post->{title};
139 my $description = edit_string($post->{description});
140 if(defined($description)) {
141 $mt->editPost($postid, { 'title' => $title, 'description' => $description });
142 $mt->setPostCategories($postid, $opts{category} ? [split(/[, ]/, $opts{category})] : $opts{category}) || warn "$!"
143 if(defined($opts{'category'}));
144 $mt->publishPost($postid);
146 die "No change. Aborting.\n";
151 if ($ARGV[0] eq "help") {
152 Getopt::Long::HelpMessage();
153 } elsif ($ARGV[0] eq "categories") {
156 } elsif ($ARGV[0] eq "list" && $ARGV[1] =~ /^\d*$/) {
158 print_recent($ARGV[1]);
159 } elsif ($ARGV[0] eq "new") {
162 } elsif ($ARGV[0] eq "edit" && $ARGV[1] =~ /^\d+$/) {
165 } elsif ($ARGV[0] eq "show" && $ARGV[1] =~ /^\d+$/) {
169 Getopt::Long::HelpMessage();
175 blogcl - Blog from the command-line
179 B<blogcl> S<[ B<--title>|B<-t> "Title" ]> S<[ B<--category>|B<-c> category[,...] ]> S<[ B<--file>|B<-f> F<file> ]> S<[ B<--username>|B<-l> username ]> S<[ B<--password>|B<-p> password ]> S<[ B<--url>|B<-u> url ]> S<[ B<--blogid>|B<-b> blogid ]> { B<new> | B<edit> postid | B<show> postid | B<list> [count] | B<categories> }
183 This is a simple script used to blog from the command-line. It was designed
184 for use with Drupal, but may work with any blog featuring a Movable Type
185 compatible interface.
187 One of the commands below must be present.
193 Creates a new blog entry.
197 Edit an existing blog entry. An existing postid must be specified. To find a
198 post's postid, use B<list>.
202 Shows the contents of the post specified by the postid given on the
207 List recents posts and their postids. An optional integer specifies the number
212 Gives categories that are valid parameters to the B<--category> option.
216 Show usage information.
224 =item B<-b>, B<--blogid>
226 Specify a blog ID. Defaults to the first blog found.
228 =item B<-c>, B<--category>
230 Specify a category for a new or existing entry. For multiple categories,
231 seperate them with a comma. Ignored except when used with either B<new> or
234 =item B<-f>, B<--file>
236 When used with B<new> or B<edit>, specifies a file from which to read the
237 post's body. When used with B<show>, specifies a file to which to write the
240 =item B<-l>, B<--username>
244 =item B<-p>, B<--password>
248 =item B<-t>, B<--title>
250 Specify a title for a new or existing entry. Ignored except when used with
251 either B<new> or B<edit>.
253 =item B<-u>, B<--url>
255 Specify the URL of the XMLRPC interface.
265 Contains options of format parameter=value. Only the long form of
266 options may be used. Omit leading hyphens.
276 Error checking is minimal.
280 Copyright by Tim Pope. All rights reserved.
282 This program is a free software; you may redistribute it and/or modify it under
283 the same terms as perl itself.
287 Tim Pope, E<lt>perl@relongto.usE<gt>.
289 L<http://www.sexygeek.org/>