3 # -*- perl -*- vim: ft=perl sw=4 sts=4
5 # For documentation, run perldoc blogcl
12 use vars qw(%opts $mt);
14 if (-r $ENV{HOME} . "/.blogclrc") {
15 open CONFIG, $ENV{HOME} . "/.blogclrc";
18 next unless m/^([^=]*)=(.*)/;
24 Getopt::Long::Configure ("bundling", "auto_help");
25 die "Invalid arguments\n" unless
26 GetOptions (\%opts, 'username|l=s', 'password|p=s', 'url|u=s', 'blogid|b=i', 'title|t=s', 'category|c=s', 'file|f=s');
29 die "No url given.\n" unless(defined($opts{url}));
30 die "No username given.\n" unless(defined($opts{username}));
31 die "No passowrd given.\n" unless(defined($opts{password}));
32 $mt = new Net::MovableType($opts{'url'}) || die "$!";
33 $mt->username($opts{'username'});
34 $mt->password($opts{'password'});
35 my $blogid = $opts{'blogid'} || $mt->resolveBlogId($opts{'username'});
37 my $userblogs=$mt->getUsersBlogs();
38 if($userblogs && $userblogs->[0]->{blogid}) {
39 $blogid=$userblogs->[0]->{'blogid'};
41 warn "Blog ID unknown.\n";
48 my $entries = $mt->getRecentPosts(shift || 10);
49 while ( my $entry = shift @$entries ) {
50 printf("[%02d] - %s\n",
51 $entry->{postid}, $entry->{title} );
53 # postid userid content title description dateCreated
56 sub print_categories {
57 print join("\n",map {$_->{categoryName}} @{$mt->getCategoryList()}), "\n";
62 my $post = $mt->getPost($postid) or die "$!";
63 my $categories = $mt->getPostCategories($postid) or die "$!";
64 if(defined($opts{file})) {
65 if($opts{file} eq "-") {
66 print STDOUT $post->{description}, "\n";
68 open FH, ">".$opts{file} or die "$!";
69 print $opts{file}, "\n";
70 print FH $post->{description}, "\n";
74 $mt->deletePost($postid,1);
79 my $post = $mt->getPost($postid) or die "$!";
80 my $categories = $mt->getPostCategories($postid) or die "$!";
81 if(defined($opts{file})) {
82 if($opts{file} eq "-") {
83 print STDOUT $post->{description}, "\n";
85 open FH, ">".$opts{file} or die "$!";
86 print $opts{file}, "\n";
87 print FH $post->{description}, "\n";
91 printf("Title: %s (%d)\n", $post->{title}, $post->{postid});
94 } elsif (@$categories==1) {
97 foreach(@$categories) {
98 print " ", $_->{'categoryName'};
100 print "\n" if(@$categories);
102 print $post->{description}, "\n";
108 if(defined($opts{file})) {
109 open FILE, $opts{file} or die "$!";
110 chop($string = join("", <FILE>));
114 my $editor = $ENV{VISUAL} || $ENV{EDITOR};
115 $editor ||= "/usr/bin/sensible-editor" if (-x "/usr/bin/sensible-editor");
116 $editor ||= "/bin/vi" if (-x "/bin/vi");
117 $editor ||= "/usr/bin/nano" if (-x "/usr/bin/nano");
118 my $tmp = new File::Temp(SUFFIX => '.html');
119 print $tmp $string, "\n";
121 my $mtime = (stat($tmp->filename))[9];
122 system(split (/ /, $editor), $tmp->filename);
123 if($mtime == (stat($tmp->filename))[9]) {
127 chop($string = join("", <$tmp>));
134 my $title = $opts{title} || Complete('Title: ');
135 my $category = $opts{category} || Complete('Categories: ', map {$_->{categoryName}} @{$mt->getCategoryList()});
136 die "No title given.\n" unless($title);
137 my $description = edit_string("");
138 if(defined($description)) {
139 my $postid = $mt->newPost({ 'title' => $title, 'description' => $description });
140 $mt->setPostCategories($postid, $category ? [split(/[, ]+/, $category)] : $category) || warn "$!"
141 if(defined($category));
142 $mt->publishPost($postid);
144 die "No change. Aborting.\n";
150 my $post = $mt->getPost($postid);
151 my $categories = $mt->getPostCategories($postid);
152 my $title = $opts{title} || Complete('Title: ', $post->{title}) || $post->{title};
153 my $description = edit_string($post->{description});
154 if(defined($description)) {
155 $mt->editPost($postid, { 'title' => $title, 'description' => $description });
156 $mt->setPostCategories($postid, $opts{category} ? [split(/[, ]/, $opts{category})] : $opts{category}) || warn "$!"
157 if(defined($opts{'category'}));
158 $mt->publishPost($postid);
160 die "No change. Aborting.\n";
165 if ($ARGV[0] eq "help") {
166 Getopt::Long::HelpMessage();
167 } elsif ($ARGV[0] eq "categories") {
170 } elsif ($ARGV[0] eq "list" && $ARGV[1] =~ /^\d*$/) {
172 print_recent($ARGV[1]);
173 } elsif ($ARGV[0] eq "new") {
176 } elsif ($ARGV[0] eq "edit" && $ARGV[1] =~ /^\d+$/) {
179 } elsif ($ARGV[0] eq "show" && $ARGV[1] =~ /^\d+$/) {
182 } elsif ($ARGV[0] eq "delete" && $ARGV[1] =~ /^\d+$/) {
184 delete_post($ARGV[1])
186 Getopt::Long::HelpMessage();
192 blogcl - Blog from the command-line
196 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<delete> postid | B<list> [count] | B<categories> }
200 This is a simple script used to blog from the command-line. It was designed
201 for use with Drupal, but may work with any blog featuring a Movable Type
202 compatible interface.
204 One of the commands below must be present.
210 Creates a new blog entry.
214 Edit an existing blog entry. An existing postid must be specified. To find a
215 post's postid, use B<list>.
219 Shows the contents of the post specified by the postid given on the
224 Deletes a post. Specify a B<--file> option to back up the contents (but not
225 the title or category) of the post.
229 List recents posts and their postids. An optional integer specifies the number
234 Gives categories that are valid parameters to the B<--category> option.
238 Show usage information.
246 =item B<-b>, B<--blogid>
248 Specify a blog ID. Defaults to the first blog found.
250 =item B<-c>, B<--category>
252 Specify a category for a new or existing entry. For multiple categories,
253 seperate them with a comma. Ignored except when used with either B<new> or
256 =item B<-f>, B<--file>
258 When used with B<new> or B<edit>, specifies a file from which to read the
259 post's body. When used with B<show>, specifies a file to which to write the
262 =item B<-l>, B<--username>
266 =item B<-p>, B<--password>
270 =item B<-t>, B<--title>
272 Specify a title for a new or existing entry. Ignored except when used with
273 either B<new> or B<edit>.
275 =item B<-u>, B<--url>
277 Specify the URL of the XMLRPC interface.
287 Contains options of format parameter=value. Only the long form of
288 options may be used. Omit leading hyphens.
298 Error checking is minimal.
302 Copyright by Tim Pope. All rights reserved.
304 This program is free software; you may redistribute it and/or modify it under
305 the same terms as perl itself.
309 Tim Pope, E<lt>perl@relongto.usE<gt>.
311 L<http://www.sexygeek.org/>