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