1package Net::MovableType;
2
3# MovableType.pm,v 1.18 2004/08/14 08:31:32 sherzodr Exp
4
5use strict;
6use vars qw($VERSION $errstr $errcode);
7use Carp;
8use XMLRPC::Lite;
9
10$VERSION = '1.74';
11
12# Preloaded methods go here.
13
14sub new {
15  my $class = shift;
16  $class = ref($class) || $class;
17
18  my ($url, $username, $password) = @_;
19
20  my $self = {
21    proxy   => undef,
22    blogid   => undef,
23    username => $username,
24    password => $password
25  };
26
27  bless $self, $class;
28
29  # if $url starts with 'http://' and ends in '.xml', we assume it was a
30  # location of rsd.xml file
31  if ( $url =~ m/^http:\/\/.+\.xml$/ ) {
32      $self->rsd_url($url) or return undef;
33
34  # if the URL just starts with 'http://', we assume it was a url for
35  # MT's XML-RPC server
36  } elsif ( $url =~ m/^http:\/\// ) {
37      $self->{proxy} = XMLRPC::Lite->proxy($url);
38
39  # in neither case, we assume it was a file system location of rsd.xml file
40  } elsif ( $url ) {
41      $self->rsd_file($url) or return undef;
42
43  }
44
45  return $self
46}
47
48
49
50
51
52# shortcut for XMLRPC::Lite's call() method. Main difference from original call()
53# is, it returns native Perl data, instead of XMLRPC::SOM object
54sub call {
55    my ($self, $method, @args) = @_;
56
57    unless ( $method ) {
58        die "call(): usage error"
59    }
60
61    my $proxy = $self->{proxy} or die "'proxy' is missing";
62    my $som   = $proxy->call($method, @args);
63    my $result= $som->result();
64
65    unless ( defined $result ) {
66        $errstr = $som->faultstring;
67        $errcode= $som->faultcode;
68        return undef
69    }
70
71    return $result
72}
73
74
75
76
77sub process_rsd {
78    my ($self, $string_or_file ) = @_;
79
80    unless ( $string_or_file ) {
81        croak "process_rsd() usage error"
82    }
83
84    require XML::Simple;
85    my $xml     = XML::Simple::XMLin(ref($string_or_file) ? $$string_or_file : $string_or_file );
86    my $apilink = $xml->{service}->{apis}->{api}->{MetaWeblog}->{apiLink};
87    my $blogid  = $xml->{service}->{apis}->{api}->{MetaWeblog}->{blogID};
88
89    unless ( $apilink && $blogid ) {
90        croak "Couldn't retrieve 'apiLink' and 'blogID' from $xml"
91    }
92
93    $self->blogId($blogid);
94    $self->{proxy} = XMLRPC::Lite->proxy($apilink);
95
96    # need to return a true value indicating success
97    return 1
98}
99
100
101# fetches RSD file from a remote location,
102# and configures Net::MovableType object properly
103sub rsd_url {
104    my ($self, $url) = @_;
105
106    unless ( $url ) {
107        croak "rsd_url() usage error"
108    }
109
110    $self->{rsd_url} = $url;
111
112    require LWP::UserAgent;
113
114    my $ua = LWP::UserAgent->new();
115    my $req= HTTP::Request->new('GET', $url);
116    my $response = $ua->request($req);
117    if ( $response->is_error ) {
118        $errstr = $response->base . ": " . $response->message;
119        $errcode= $response->code;
120        return undef
121    }
122
123    return $self->process_rsd($response->content_ref)
124}
125
126
127
128sub rsd_file {
129    my ($self, $file) = @_;
130
131    unless ( $file ) {
132        croak "rsd_file() usage error"
133    }
134
135    $self->{rsd_file} = $file;
136
137    return $self->process_rsd($file)
138}
139
140
141
142
143sub username {
144    my ($self, $username) = @_;
145
146    if ( defined $username ) {
147        $self->{username} = $username;
148    }
149    return $self->{username}
150}
151
152
153
154*error = \&errstr;
155sub errstr {
156    return $errstr
157}
158
159
160sub errcode {
161    return $errcode
162}
163
164
165
166
167sub password {
168  my ($self, $password) = @_;
169
170  if ( defined $password ) {
171    $self->{password} = $password
172  }
173  return $self->{password}
174}
175
176
177
178sub proxy {
179    my ($self, $proxy) = @_;
180
181    if ( defined $proxy ) {
182        $self->{proxy} = $proxy
183    }
184    return $self->{proxy}
185}
186
187
188
189*blogid = \&blogId;
190sub blogId {
191    my ($self, $blogid) = @_;
192
193    if ( defined $blogid ) {
194        $self->{blogid} = $blogid
195    }
196    return $self->{blogid}
197}
198
199
200
201sub resolveBlogId {
202    my ($self, $blogname) = @_;
203
204    unless ( $self->username && $self->password ) {
205        croak "username and password are missing\n"
206    }
207
208    my $blogs = $self->getUsersBlogs();
209    while ( my $b = shift @$blogs ) {
210        if ( $b->{blogName} eq $blogname ) {
211            return $b->{blogid}
212        }
213    }
214
215    $errstr = "Couldn't find blog '$blogname'";
216    return undef
217}
218
219
220
221
222sub getBlogInfo {
223    my ($self, $blogid) = @_;
224
225    $blogid ||= $self->blogId() or croak "no 'blogId' set";
226    my $blogs = $self->getUsersBlogs() or return undef;
227
228    while ( my $b = shift @$blogs ) {
229        if ( $b->{blogid} == $blogid ) {
230            return $b
231        }
232    }
233
234    $errstr = "No blog found with id '$blogid";
235    return undef
236}
237
238
239
240
241
242
243
244*getBlogs = \&getUsersBlogs;
245sub getUsersBlogs {
246    my ($self, $username, $password)  = @_;
247
248    $username = $self->username($username);
249    $password = $self->password($password);
250
251    unless ( $username && $password ) {
252        croak "username and password are missing";
253    }
254
255    return $self->call('blogger.getUsersBlogs', "", $username, $password)
256}
257
258
259
260
261
262
263sub getUserInfo {
264    my ($self, $username, $password) = @_;
265
266    $username = $self->username($username);
267    $password = $self->password($password);
268
269    unless ( $username && $password ) {
270        croak "username and/or password are missing"
271    }
272
273    return $self->call('blogger.getUserInfo', "", $username, $password)
274}
275
276
277
278
279sub getPost {
280    my ($self, $postid, $username, $password) = @_;
281
282    $username = $self->username($username);
283    $password = $self->password($password);
284
285    unless ( $username && $password && $postid ) {
286        croak "getPost() usage error"
287    }
288
289    return $self->call('metaWeblog.getPost', $postid, $username, $password)
290}
291
292
293
294
295
296
297
298sub getRecentPosts {
299    my ($self, $numposts) = @_;
300
301    my $blogid   = $self->blogId()     or croak "no 'blogId' defined";
302    my $username = $self->username()   or croak "no 'username' defined";
303    my $password = $self->password()   or croak "no 'password' defined";
304    $numposts ||= 1;
305
306    return $self->call('metaWeblog.getRecentPosts', $blogid, $username, $password, $numposts)
307}
308
309
310
311sub getRecentPostTitles {
312    my ($self, $numposts) = @_;
313
314    my $blogid  = $self->blogId()       or croak "no 'blogId' defined";
315    my $username= $self->username()     or croak "no 'username' defined";
316    my $password= $self->password()     or croak "no 'password' defined";
317    $numposts ||= 1;
318
319    return $self->call('mt.getRecentPostTitles', $blogid, $username, $password, $numposts)
320}
321
322
323
324
325
326
327*getCategories = \&getCategoryList;
328sub getCategoryList {
329    my ($self, $blogid, $username, $password) = @_;
330
331    $blogid      = $self->blogId($blogid) or croak "no 'blogId' defined";
332    $username   = $self->username($username) or croak "no 'username' defined";
333    $password   = $self->password($password) or croak "no 'password' defined";
334
335    return $self->call('mt.getCategoryList', $blogid, $username, $password)
336}
337
338
339
340
341sub getPostCategories {
342    my ($self, $postid, $username, $password) = @_;
343
344    $username = $self->username($username) or croak "no 'username' defined";
345    $password = $self->password($password) or croak "no 'password' defined";
346
347    unless ( $postid ) {
348        croak "getPostCategories() usage error"
349    }
350
351    return $self->call('mt.getPostCategories', $postid, $username, $password)
352}
353
354
355
356sub setPostCategories {
357    my ($self, $postid, $cats) = @_;
358
359    unless ( ref $cats ) {
360        $cats = [$cats]
361    }
362
363    unless ( @$cats && $postid ) {
364        croak "setPostCategories() usage error"
365    }
366
367    my $blogid = $self->blogId()    or croak "no 'blogId' set";
368
369    my $category_list = $self->getCategoryList($blogid);
370    my $post_categories = [];
371    for my $cat ( @$cats ) {
372        for my $c ( @$category_list ) {
373            if ( lc $c->{categoryName} eq lc $cat ) {
374                push @$post_categories, {categoryId=>$c->{categoryId} }
375            }
376        }
377    }
378
379    my $username  = $self->username() or croak "no 'username' defined";
380    my $password  = $self->password() or croak "no 'password' defined";
381    $postid                          or croak "setPostCategories() usage error";
382
383    return $self->call('mt.setPostCategories', $postid, $username, $password, $post_categories)
384}
385
386
387
388
389
390
391
392
393
394
395
396sub supportedMethods {
397    my ($self) = @_;
398
399    return $self->call('mt.supportedMethods')
400}
401
402
403
404sub publishPost {
405    my ($self, $postid, $username, $password) = @_;
406
407    $username = $self->username($username) or croak "no 'username' set";
408    $password = $self->password($password)  or croak "no 'password' set";
409
410    unless ( $postid ) {
411        croak "publishPost() usage error"
412    }
413
414    return $self->call('mt.publishPost', $postid, $username, $password)
415}
416
417
418
419
420
421sub newPost {
422    my ($self, $content, $publish) = @_;
423
424    my $blogid   = $self->blogId()   or croak "'blogId' is missing";
425    my $username = $self->username() or croak "'username' is not set";
426    my $password = $self->password() or croak "'password' is not set";
427
428    unless ( $content && (ref($content) eq 'HASH') ) {
429        croak "newPost() usage error"
430    }
431
432    return $self->call('metaWeblog.newPost', $blogid, $username, $password, $content, $publish)
433}
434
435
436
437
438
439
440sub editPost {
441    my ($self, $postid, $content, $publish) = @_;
442
443    my $username = $self->username() or croak "'username' is not set";
444    my $password = $self->password() or croak "'password' is not set";
445
446    unless ( $content && (ref($content) eq 'HASH') ) {
447        croak "newPost() usage error"
448    }
449
450    return $self->call('metaWeblog.editPost', $postid, $username, $password, $content, $publish)
451}
452
453
454
455
456
457
458sub deletePost {
459    my ($self, $postid, $publish) = @_;
460
461    my $username = $self->username or croak "'username' not set";
462    my $password = $self->password or croak "'password' not set";
463    $postid                        or croak "deletePost() usage error";
464
465    return $self->call('blogger.deletePost', "", $postid, $username, $password, $publish)
466}
467
468
469
470
471*upload = \&newMediaObject;
472sub newMediaObject {
473    my ($self, $filename, $name, $type) = @_;
474
475    my $blogid   = $self->blogId()   or croak "'blogId' is missing";
476    my $username = $self->username() or croak "'username' is not set";
477    my $password = $self->password() or croak "'password' is not set";
478
479    unless ( $filename ) {
480        croak "newMediaObject() usage error";
481    }
482
483    my $blob = undef;
484    if ( ref $filename ) {
485        $blob = $$filename;
486        $filename = undef;
487
488    } else {
489        unless(open(FH, $filename)) {
490            $errstr = "couldn't open $filename: $!";
491            return undef
492        }
493        local $/ = undef;
494        $blob = <FH>; close(FH);
495    }
496
497    if ( $filename && !$name ) {
498        require File::Basename;
499        $name = File::Basename::basename($filename);
500    }
501
502    unless ( $name ) {
503        croak "newMediaObject() usage error: \$name is missing"
504    }
505
506    my %content_hash = (
507         bits    => XMLRPC::Data->type(base64 => $blob),
508         name    => $name,
509         type    => $type || ""
510    );
511
512    return $self->call('metaWeblog.newMediaObject', $blogid, $username, $password, \%content_hash)
513}
514
515
516
517
518
519
520
521
522
523
524sub dump {
525  my $self = shift;
526
527  require Data::Dumper;
528  my $d = new Data::Dumper([$self], [ref $self]);
529  return $d->Dump();
530}
531
532
533
534package MovableType;
535@MovableType::ISA = ('Net::MovableType');
536
537
538package MT;
539@MT::ISA = ('Net::MovableType');
540
541
5421;
543__END__
544# Below is stub documentation for your module. You better edit it!
545
546=head1 NAME
547
548Net::MovableType - light-weight MovableType client
549
550=head1 SYNOPSIS
551
552  use Net::MovableType;
553  my $mt = new Net::MovableType('http://your.com/rsd.xml');
554  $mt->username('user');
555  $mt->password('secret');
556
557  my $entries = $mt->getRecentPosts(5);
558  while ( my $entry = shift @$entries ) {
559    printf("[%02d] - %s\n\tURI: %s\n",
560           $entry->{postid}, $entry->{title}, $entry->{'link'} )
561  }
562
563=head1 DESCRIPTION
564
565Using I<Net::MovableType> you can post new entries, edit existing entries, browse entries
566and users blogs, and perform most of the features you can perform through accessing your
567MovableType account.
568
569Since I<Net::MovableType> uses MT's XML-RPC (I<Remote Procedure Call> gateway, you can do it from
570any computer with Internet connection.
571
572=head1 PROGRAMMING STYLE
573
574I<Net::MovableType> promises an intuitive, user friendly, Object Oriented interface for managing
575your web sites published through MovableType. Most of the method names correspond to those documented
576in MovableType's Programming Interface Manual, however, their expected arguments differ.
577
578=head2 CREATING MT OBJECT
579
580Before you start doing anything, you need to have a I<MovableType> object handy. You can
581create a I<MovableType> object by calling C<new()> - constructor method:
582
583    $mt = new MovableType('http://mt.handalak.com/cgi-bin/mt-xmlrpc.cgi');
584    # or
585    $mt = new MovableType('http://author.handalak.com/rsd.xml');
586    # or even..
587    $mt = new MovableType('/home/sherzodr/public_html/author/rsd.xml');
588
589Notice, you need to pass at least one argument while creating I<MT> object, that is
590the location of your either F<mt-xmlrpc.cgi> file, or your web site's F<rsd.xml> file.
591Default templates of I<MT> already generate F<rsd.xml> file for you. If they don't,
592you should get one from http://www.movabletype.org/
593
594If your F<rsd.xml> file is available locally, you should provide a full path to the
595file instead of providing it as a URL. Reading the file locally is more efficient
596than fetching it over the Web.
597
598Giving it a location of your F<rsd.xml> file is preferred, since it will ensure that
599your C<blogId()> will be set properly. Otherwise, you will have to do it manually calling
600C<blogId()> (see later).
601
602It is very important that you get this one right. Otherwise, I<Net::MovableType> will
603know neither about where your web site is nor how to access them.
604
605I<MovableType> requires you to provide valid username/password pair to do most of the things.
606So you need to tell I<MovableType> object about your username and passwords, so it can use
607them to access the resources.
608
609=head2 LOGGING IN
610
611You can login in two ways; by either providing your I<username> and I<password> while creating
612I<MT> object, or by calling C<username()> and C<password()> methods after creating I<MT> object:
613
614    # creating MT object with valid username/password:
615    $mt = new MovableType($proxy, 'author', 'password');
616
617    # or
618    $mt = new MovableType($proxy);
619    $mt->username('author');
620    $mt->password('password');
621
622C<username()> and C<password()> methods are used for both setting username and password,
623as well as for retrieving username and password for the current user. Just don't pass
624it any arguments should you wish to use for the latter purpose.
625
626=head2 DEFINING A BLOG ID
627
628Defining a blog id may not be necessary if you generated your C<$mt> object
629with an F<rsd.xml> file. Otherwise, read on.
630
631As we will see in subsequent sections, most of the I<MovableType>'s methods operate on
632specific web log. For defining a default web log to operate on, after setting above I<username>
633and I<password>, you can also set your default blog id using C<blogId()> method:
634
635    $mt->blogId(1);
636
637To be able to do this, you first need to know your blog id. There are no documented ways of
638retrieving your blog id, except for investigating the URL of your MovableType account panel.
639Just login to your MovableType control panel (through F<mt.cgi> script). In the first screen,
640you should see a list of your web logs. Click on the web log in question, and look at the
641URL of the current window. In my case, it is:
642
643    http://mt.handalak.com/cgi-bin/mt?__mode=menu&blog_id=1
644
645Notice I<blog_id> parameter? That's the one!
646
647Wish you didn't have to go through all those steps to find out your blog id? I<Net::MovableType>
648provides C<resolveBlogId()> method, which accepts a name of the web log, and returns correct blogId:
649
650    $blog_id = $mt->resolveBlogId('lost+found');
651    $mt->blogId($blog_id);
652
653Another way of retrieving information about your web logs is to get all the lists of your web logs
654by calling C<getUsersBlogs()> method:
655
656    $blogs = $mt->getUsersBlogs();
657
658C<getUsersBlogs()> returns list of blogs, where each blog is represented with a hashref. Each hashref
659holds such information as I<blogid>, I<blogName> and I<url>. Following example lists all the
660blogs belonging to the current user:
661
662    $blogs = $mt->getUsersBlogs();
663    for $b ( @$blogs ) {
664        printf("[%02d] %s\n\t%s\n", $b->{blogid}, $b->{blogName}, $b->{url})
665    }
666
667=head2 POSTING NEW ENTRY
668
669By now, you know how to login and how to define your blogId. Now is a good time to post
670a new article to your web log. That's what  C<newPost()> method is for.
671
672C<newPost()> expects at least a single argument, which should be a reference to a hash
673containing all the details of your new entry. First, let's define a new entry to be posted
674on our web log:
675
676    $entry = {
677        title       => "Hello World from Net::MovableType",
678        description => "Look ma, no hands!"
679    };
680
681Now, we can pass above C<$entry> to our C<newPost()> method:
682
683    $mt->newPost($entry);
684
685In the above example, I<description> field corresponds to Entry Body field of MovableType.
686This is accessible from within your templates through I<MTEntryBody> tag. MovableType allows
687you to define more entry properties than we did above. Following is the list of all the
688attributes we could've defined in our above C<$entry>:
689
690=over 4
691
692=item dateCreated
693
694I<Authored Date> attribute of the entry. Format of the date should be in I<ISO.8601> format
695
696=item mt_allow_comments
697
698Should comments be allowed for this entry
699
700=item mt_allow_pings
701
702should pings be allowed for this entry
703
704=item mt_convert_breaks
705
706Should it use "Convert Breaks" text formatter?
707
708=item mt_text_more
709
710Extended entry
711
712=item mt_excerpt
713
714Excerpt of the entry
715
716=item mt_keywords
717
718Keywords for the entry
719
720=item mt_tb_ping_urls
721
722List of track back ping urls
723
724=back
725
726Above entry is posted to your MT database. But you still don't see it in your weblog, do you?
727It's because, the entry is still not published. There are several ways of publishing an entry.
728If you pass a true value to C<newPost()> as the second argument, it will publish your entry
729automatically:
730
731    $mt->newPost($entry, 1);
732
733You can also publish your post by calling C<publishPost()> method. C<publishPost()>, however, needs
734to know I<id> of the entry to publish. Our above C<newPost()>, luckily, already returns this information,
735which we've been ignoring until now:
736
737    my $new_id = $mt->newPost($entry);
738    $mt->publishPost($new_id);
739
740You can also publish your post later, manually, by simply rebuilding your web log from within
741your MT control panel.
742
743=head2 ENTRY CATEGORIES
744
745I<MovableType> also allows entries to be associated with specific category, or even with
746multiple categories. For example, above C<$entry>, we just published, may belong to category "Tutorials".
747
748Unfortunately, structure of our C<$entry> doesn't have any slots for defining its categories.
749This task is performed by a separate procedure, C<setPostCategories()>.
750
751C<setPostCategories()> expects two arguments. First should be I<postid> of the post to assign
752categories to, and second argument should either be a name of the primary category, or
753a list of categories in the form of an arrayref. In the latter case, the first category mentioned
754becomes entry's primary category.
755
756For example, let's re-post our above C<$entry>, but this time assign it to "Tutorials" category:
757
758    $new_id = $mt->newPost($entry, 0);  # <-- not publishing it yet
759    $mt->setPostCategories($new_id, "Tutorials");
760    $mt->publishPost($new_id);
761
762We could also assign a single entry to multiple categories. Say, to both "Tutorials" and
763"Daily Endeavors". But say, we want "Daily Endeavors" to be the primary category for this entry:
764
765    $new_id = $mt->newPost($entry, 0);  # <-- not publishing it yet
766    $mt->setPostCategories($newPid, ["Daily Endeavors", "Tutorials"]);
767    $mt->publishPost($new_id);
768
769
770Notice, in above examples we made sure that C<newPost()> method didn't publish the entry
771by passing it false value as the second argument. If we published it, we again would end
772up having to re-publish the entry after calling C<setPostCategories()>, thus wasting
773unnecessary resources.
774
775=head2 BROWSING ENTRIES
776
777Say, you want to be able to retrieve a list of entries from your web log. There couple of ways
778for doing this. If you just want titles of your entries, consider using C<getRecentPostTitles()>
779method. C<getRecentPostTitles()> returns an array of references to a hash, where each hashref
780contains fields I<dateCreated>, I<userid>, I<postid> and I<title>.
781
782C<getRecentPostTitles()> accepts a single argument, denoting the number of recent entries to retrieve.
783If you don't pass any arguments, it defaults to I<1>:
784
785    $recentTitles = $mt->getRecentPostTitles(10);
786    for my $post ( @$resentTitles ) {
787        printf("[%03d] %s\n", $post->{postid}, $post->{title})
788    }
789
790Remember, even if you don't pass any arguments to C<getRecentPostTitles()>, it still returns an array
791of hashrefs, but this array will hold only one element:
792
793    $recentTitle = $mt->getRecentPostTitles();
794    printf("[%03d] %s\n", $recentTitles->[0]->{postid}, $recentTitles->[0]->{title});
795
796Another way of browsing a list of entries, is through C<getRecentPosts()> method. Use of this method
797is identical to above-discussed C<getRecentPostTitles()>, but this one returns a lot more information
798about each post. It can accept a single argument, denoting number of recent entries to retrieve.
799
800Elements of the returned hash are compatible with the C<$entry> we constructed in earlier sections.
801
802=head2 RETREIVING A SINGLE ENTRY
803
804Sometimes, you may want to retrieve a specific entry from your web log. That's what C<getPost()>
805method does. It accepts a single argument, denoting an id of the post, and returns a hashref, keys of
806which are compatible with the C<$entry> we built in earlier sections (see POSTING NEW ENTRY):
807
808    my $post = $mt->getPost(134);
809    printf("Title: %s (%d)\n", $post->{title}, $post->{postid});
810    printf("Excerpt: %s\n\n", $post->{mt_excerpt} );
811    printf("BODY: \n%s\n", $post->{description});
812    if ( $post->{mt_text_more} ) {
813        printf("\nEXTENDED ENTRY:\n", $post->{mt_text_more} );
814    }
815
816=head2 EDITING ENTRY
817
818Editing an entry means to re-post the entry. This is done almost the same way as the entry
819has been published. C<editPost()> method, which is very similar in use to C<newPost()>, but accepts
820a I<postid> denoting the id of the post that you are editing. Second argument should be a hashref,
821describing fields of the entry. Structure of this hashref was discussed in earlier sections (see
822POSTING NEW ENTRY):
823
824    $mt->editPost($postid, $entry)
825
826
827=head2 DELETING ENTRY
828
829You can delete a specific entry from your database (and weblog) using C<deletePost()>
830method. C<deletePost()> accepts at least one argument, which is the id of the post to be
831deleted:
832
833    $mt->deletePost(122);   # <-- deleting post 122
834
835
836By default entries are deleted form the database, not from your web log. They usually
837fade away once your web log is rebuilt. However, it may be more desirable to remove
838the entry both from the database and from the web site at the same time.
839
840This can be done by passing a true value as the second argument to C<deletePost()>. This
841ensures that your pages pertaining to the deleted entry are rebuilt:
842
843    $mt->deletePost(122, 1); # <-- delet post 122, and rebuilt the web site
844
845
846=head2 UPLOADING
847
848With I<Net::MovableType>, you can also upload files to your web site. Most common
849use of this feature is to associate an image, or some other downloadable file with
850your entries.
851
852I<Net::MovableType> provides C<upload()> method, which given a file contents,
853uploads it to your web site's F<archives> folder. On success, returns the URL of
854the newly uploaded file.
855
856C<upload()> method accepts either a full path to your file, or a reference to its
857contents. Second argument to upload() should be the file's name. If you already provided
858file's full path as the first argument, I<Net::MovableType> resolves the name of the file
859automatically, if it's missing.
860
861If you passed the contents of the file as the first argument, you are required to provide
862the name of the file explicitly.
863
864Consider the following code, which uploads a F<logo.gif> file to your web site:
865
866    $url = $mt->upload('D:\images\logo.gif');
867
868Following example uploads the same file, but saves it as "my-log.gif", instead of
869"logo.gif":
870
871
872    $url = $mt->upload('D:\images\logo.gif', 'my-logo.gif');
873
874
875Following example downloads a file from some remote location, using LWP::Simple,
876and uploads it to your web site with name "image.jpeg":
877
878
879    use LWP::Simple;
880
881    $content = get('http://some.dot.com/image.jpeg');
882    $url = $mt->upload( \$content, 'image.jpeg' )
883
884
885=head1 ERROR HANDLING
886
887If you noticed, we didn't even try to check if any of our remote procedure calls
888succeeded. This is to keep the examples as clean as possible.
889
890For example, consider the following call:
891
892    $new_id = $mt->newPost($entry, 1);
893
894There is no guarantee that the above entry is posted, nor published.
895You username/password might be wrong, or you made a mistake while defining your
896I<mt-xmlrpc> gateway? You may never know until its too late.
897
898That's why you should always check the return value of the methods that make a remote
899procedure call.
900
901All the methods return true on success, C<undef> otherwise. Error message from the latest
902procedure call is available by calling C<errstr()> static class method. Code of the error
903message (not always as useful) can be retrieved through C<errcode()> static class method:
904
905    $new_id = $mt->newPost($entry, 1);
906    unless ( defined $new_id ) {
907        die $mt->errstr
908    }
909
910or just:
911
912    $new_id = $mt->newPost($entry, 1) or die $mt->errstr;
913
914
915If you are creating your I<MovableType> object with an F<rsd.xml> file, you should also
916check the return value of C<new()>:
917
918    $mt = new Net::MovableType($rsd_url);
919    unless ( defined $mt ) {
920        die "couldn't create MT object with $rsd_url: " . Net::MovableType->errstr
921    }
922
923
924=head1 OTHER METHODS
925
926comming soon...
927
928=head1 TEST BLOG
929
930I opened a public test blog at http://net-mt.handalak.com/. Initial purpose of this
931blog was to provide a working weblog for Net::MovableType's tests to operate on. Currently
932its open to the World.
933
934Credentials needed for accessing this weblog through Net::MovableType are:
935
936    Username: net-mt
937    Password: secret
938
939=head1 TODO
940
941Should implement a caching mechanism
942
943Manual is still not complete, more methods are left to be documented properly
944
945
946=head1 CREDITS
947
948Following people have contributed to the library with their suggestions and patches.
949The list may not be complete. Please help me with it.
950
951=over 4
952
953=item Atsushi Sano
954
955For F<rsd.xml> and C<newMediaObject()> support.
956
957=back
958
959=head1 COPYRIGHT
960
961Copyright (C) 2003, Sherzod B. Ruzmetov. All rights reserved.
962
963This library is a free software, and can be modified and distributed under the same
964terms as Perl itself.
965
966=head1 AUTHOR
967
968Sherzod Ruzmetov E<lt>sherzodr AT cpan.orgE<gt>
969
970http://author.handalak.com/
971
972=head1 SEE ALSO
973
974L<Net::Blogger>
975
976=cut
977