Index: openacs-4/packages/lars-blogger/tcl/blogger-api-init.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/tcl/blogger-api-init.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/lars-blogger/tcl/blogger-api-init.tcl	8 Dec 2003 05:55:52 -0000	1.1
@@ -0,0 +1,16 @@
+# /packages/lars-blogger/tcl/blogger-api-init.tcl
+ad_library {
+     Register Blogger API procs
+     @author Vinod Kurup [vinod@kurup.com]
+     @creation-date Fri Oct  3 23:04:15 2003
+     @cvs-id $Id: blogger-api-init.tcl,v 1.1 2003/12/08 05:55:52 vinodk Exp $
+}
+
+xmlrpc::register_proc blogger.newPost
+xmlrpc::register_proc blogger.editPost
+xmlrpc::register_proc blogger.getUsersBlogs
+xmlrpc::register_proc blogger.getUserInfo
+xmlrpc::register_proc blogger.getPost
+xmlrpc::register_proc blogger.getRecentPosts
+xmlrpc::register_proc blogger.deletePost
+
Index: openacs-4/packages/lars-blogger/tcl/blogger-api-procs-oracle.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/tcl/blogger-api-procs-oracle.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/lars-blogger/tcl/blogger-api-procs-oracle.xql	8 Dec 2003 05:55:53 -0000	1.1
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<queryset>
+   <rdbms><type>oracle</type><version>8.1.6</version></rdbms>
+
+<fullquery name="blogger.getRecentPosts.get_n_entries">
+      <querytext>
+        select * from 
+        ( select entry_id,
+               to_char(entry_date, 'YYYY-MM-DD') as entry_date,
+               content,
+               to_char(posted_date , 'HH24:MI') as posted_time_pretty
+		    from   pinds_blog_entries
+		    and    package_id = :package_id
+		    and    draft_p = 'f'
+		    and    deleted_p = 'f'
+		    order  by entry_date desc, posted_date desc
+        ) where rownum <= :numberOfPosts
+      </querytext>
+</fullquery>
+
+</queryset>
Index: openacs-4/packages/lars-blogger/tcl/blogger-api-procs-postgresql.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/tcl/blogger-api-procs-postgresql.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/lars-blogger/tcl/blogger-api-procs-postgresql.xql	8 Dec 2003 05:55:52 -0000	1.1
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<queryset>
+   <rdbms><type>postgresql</type><version>7.2</version></rdbms>
+
+<fullquery name="blogger.getRecentPosts.get_n_entries">
+      <querytext>
+        select entry_id,
+               to_char(entry_date, 'YYYY-MM-DD') as entry_date,
+               content,
+               to_char(posted_date , 'HH24:MI') as posted_time_pretty
+		    from   pinds_blog_entries 
+		    where  package_id = :package_id
+		    and    draft_p = 'f'
+		    and    deleted_p = 'f'
+		    order  by entry_date desc, posted_date desc
+            limit $numberOfPosts
+      </querytext>
+</fullquery>
+
+</queryset>
Index: openacs-4/packages/lars-blogger/tcl/blogger-api-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/tcl/blogger-api-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/lars-blogger/tcl/blogger-api-procs.tcl	8 Dec 2003 05:55:52 -0000	1.1
@@ -0,0 +1,376 @@
+# /packages/lars-blogger/tcl/blogger-api-procs.tcl
+ad_library {
+    Support the Blogger API
+    http://www.blogger.com/developers/api/1_docs/
+
+     @author Vinod Kurup [vinod@kurup.com]
+     @creation-date Fri Oct  3 21:26:11 2003
+     @cvs-id $Id: blogger-api-procs.tcl,v 1.1 2003/12/08 05:55:52 vinodk Exp $
+}
+
+# Helper procs
+
+ad_proc lars_blog_auth_for_xmlrpc {
+    -username
+    -password
+} {
+    Authenticate a user based on info from XML-RPC client. Not sure if we're
+    getting email or username, so test for @ and decide.
+
+    @returns user_id if successful authentication. errors if unsuccesful.
+    @author Vinod Kurup
+} {
+    if { [string first "@" $username] != -1 } {
+        # use email auth
+        array set auth [auth::authenticate -email $username \
+                            -password $password]
+    } else {
+        # use username auth
+        array set auth [auth::authenticate -username $username \
+                            -password $password]
+    }
+
+    if { $auth(auth_status) != "ok" } {
+        return -code error $auth(auth_message)
+    }
+
+    return $auth(user_id)
+}
+
+# Blogger API 1.0 Methods
+
+ad_proc -public blogger.newPost {
+    appkey
+    package_id
+    username
+    password
+    content
+    publish_p
+} {
+    <strong>From Blogger API documentation:</strong>
+    <p>
+    blogger.newPost makes a new post to a designated blog. Optionally, will 
+    publish the blog after making the post. On success, it returns the unique 
+    ID of the new post. On error, it will return some error message.
+    </p>
+
+    <p>
+    OpenACS Notes: lars-blogger requires title, but we don't get one from 
+    XML-RPC. So, leave title as " " for now. User will have to manually edit 
+    it later if needed.
+    </p>    
+
+    @param appkey (string): Ignored in OpenACS
+    @param package_id (string): Blog's package_id
+    @param username (string): email or username for a user who has permission 
+    to post to the blog.
+    @param password (string): Password for said username/email
+    @param content (string): Contents of the post
+    @param publish_p (boolean): If true, the blog will be published 
+    immediately after the post is made.
+
+    @return returns entry_id for new post.
+} {
+    set user_id [lars_blog_auth_for_xmlrpc \
+                     -username $username \
+                     -password $password]
+
+    permission::require_permission -party_id $user_id \
+        -object_id $package_id \
+        -privilege create
+
+    set entry_id [db_nextval t_acs_object_id_seq]
+    set entry_date [clock format [clock seconds] -format %Y-%m-%d]
+
+    return [list -string [lars_blog_entry_add -entry_id $entry_id \
+                           -package_id $package_id \
+                           -title " " \
+                           -content $content \
+                           -content_format "text/html" \
+                           -entry_date $entry_date \
+                           -draft_p [ad_decode $publish_p 1 f t]
+                      ]]
+}
+
+ad_proc -public blogger.editPost {
+    appkey
+    entry_id
+    username
+    password
+    content
+    publish_p
+} {
+    <strong>From Blogger API documentation:</strong>
+    <p>
+    blogger.editPost changes the contents of a given post. Optionally, will 
+    publish the blog the post belongs to after changing the post. On success, 
+    it returns a boolean true value. On error, it will return a fault with an 
+    error message.
+    </p>
+
+    @param appkey (string): Ignored in OpenACS
+    @param entry_id (string): Post's entry_id
+    @param username (string): email or username for a user who has permission 
+    to post to the blog.
+    @param password (string): Password for said username/email
+    @param content (string): Contents of the post
+    @param publish_p (boolean): If true, the blog will be published 
+    immediately after the post is made.
+
+    @return returns 1 if success
+} {
+    set user_id [lars_blog_auth_for_xmlrpc \
+                     -username $username \
+                     -password $password]
+
+    permission::require_permission -party_id $user_id \
+        -object_id $entry_id \
+        -privilege write
+
+    lars_blog_entry_edit -entry_id $entry_id \
+        -title " " \
+        -content $content \
+        -content_format "text/html" \
+        -entry_date [clock format [clock seconds] -format %Y-%m-%d] \
+        -draft_p [ad_decode $publish_p 1 f t]
+
+    return [list -boolean 1]
+}
+
+ad_proc -public blogger.getUsersBlogs {
+    appkey
+    username
+    password
+} {
+    <strong>From Blogger API documentation:</strong>
+    <p>
+    blogger.getUsersBlogs returns information about all the blogs a given
+    user is a member of. Data is returned as an array of &lt;struct>'s
+    containing the ID (blogid), name (blogName), and URL (url) of each
+    blog.
+    </p>
+
+    <p>
+    OpenACS Notes: Returns blogs on which user has 'create' privilege
+    </p>
+
+    @param appkey (string): Ignored in OpenACS
+    @param username (string): email or username for a user who has permission 
+    to post to the blog.
+    @param password (string): Password for said username/email
+
+    @return returns array of structs containing above information (one struct
+            per blog)
+} {
+    set user_id [lars_blog_auth_for_xmlrpc \
+                     -username $username \
+                     -password $password]
+
+    # find blogs on which this user has create permission
+
+    set return_array [list]
+    foreach package_id [lars_blog_list_user_blogs $user_id] {
+        array unset struct
+        set struct(url) [list -string \
+                             [ad_url][lars_blog_public_package_url \
+                                          -package_id $package_id]]
+        set struct(blogid) [list -string $package_id]
+        set struct(blogName) [list -string \
+                                  [lars_blog_name -package_id $package_id]]
+        
+        lappend return_array [list -struct [array get struct]]
+    }
+
+    return [list -array $return_array]
+}
+
+ad_proc -public blogger.getUserInfo {
+    appkey
+    username
+    password
+} {
+    <strong>From Blogger API documentation:</strong>
+    <p>
+    blogger.getUserInfo returns returns a struct containing user's userid, 
+    firstname, lastname, nickname, email, and url.
+    </p>
+
+    <p>
+    OpenACS Notes: I'm not going to fill nickname. I could use screename, but
+    not sure what the semantics of that is supposed to be. Going to use
+    the public 'about user' page for URL - is there a better choice?
+    </p>
+
+    @param appkey (string): Ignored in OpenACS
+    @param username (string): email or username for a user who has permission 
+    to post to the blog.
+    @param password (string): Password for said username/email
+
+    @return returns struct containing above information
+} {
+    set user_id [lars_blog_auth_for_xmlrpc \
+                     -username $username \
+                     -password $password]
+
+    array set user_info [list]
+    acs_user::get -user_id $user_id -array user_info
+    
+    array set struct []
+    set struct(nickname) [list -string ""]
+    set struct(userid) [list -string $user_id]
+    set struct(url) [list -string \
+                         [ad_url][acs_community_member_url -user_id $user_id]]
+    set struct(email) [list -string $user_info(email)]
+    set struct(lastname) [list -string $user_info(last_name)]
+    set struct(firstname) [list -string $user_info(first_names)]
+
+    return [list -struct [array get struct]]
+}
+
+# I don't see these next 3 methods in the Blogger 1.0 API, but 
+# some tools implement them. Why aren't they documented anywhere?
+# I found some documentation at 
+# http://xmlrpc.free-conversant.com/docs/bloggerAPI#getPost
+
+ad_proc -public blogger.getPost {
+    appkey
+    entry_id
+    username
+    password
+} {
+    <strong>From Blogger API documentation:</strong>
+    <p>
+    Not documented anywhere on blogger.com as of 2003-10-05.
+    </p>
+
+    <p>
+    OpenACS Notes: The Blogger API has no place to store a title. This means
+    that if you create an entry via the web interface and then getPost via
+    the XML-RPC interface, you won't retrieve the title that you entered. Each
+    client tool that I've tested handles this fault in the Blogger API 
+    differently, so there's no easy way to consistently handle all clients.
+    NetNewsWire disables the title field in its UI, which I think is the
+    correct thing to do.
+    </p>
+
+    @param appkey (string): Ignored in OpenACS
+    @param entry_id (string): Post's entry_id
+    @param username (string): email or username for a user who has permission 
+    to post to the blog.
+    @param password (string): Password for said username/email
+
+    @return struct containing values content ( message body ), userId, 
+    postId and dateCreated.
+} {
+    set user_id [lars_blog_auth_for_xmlrpc \
+                     -username $username \
+                     -password $password]
+
+    permission::require_permission -party_id $user_id \
+        -object_id $entry_id \
+        -privilege read
+
+    array set e [list]
+
+    if { ![catch {lars_blogger::entry::get -entry_id $entry_id -array e} errmsg] } {
+        # put the date in readable format
+        set posted_date "$e(entry_date) $e(posted_time_pretty)"
+
+        array set struct [list]
+        # note: Blogger has no space for title, so we ignore title
+        set struct(content) [list -string "$e(content)"]
+        set struct(userid) [list -string $user_id]
+        set struct(postid) [list -string $entry_id]
+        set struct(dateCreated) [list -date $posted_date]
+    }
+
+    return [list -struct [array get struct]]
+}
+
+ad_proc -public blogger.getRecentPosts {
+    appkey
+    package_id
+    username
+    password
+    numberOfPosts
+} {
+    <strong>From Blogger API documentation:</strong>
+    <p>
+    Not documented anywhere on blogger.com as of 2003-10-05.
+    </p>
+
+    @param appkey (string): Ignored in OpenACS
+    @param package_id (string): Blog's package_id
+    @param username (string): email or username for a user who has permission 
+    to post to the blog.
+    @param password (string): Password for said username/email
+    @param numberOfPosts (integer): Number of posts to retrieve.
+
+    @return an array of structs containing the latest n posts to a given
+    blog, newest first. Each post struct includes: dateCreated (when post 
+    was made), userid (who made the post), postid, and content.
+} {
+    set user_id [lars_blog_auth_for_xmlrpc \
+                     -username $username \
+                     -password $password]
+
+    permission::require_permission -party_id $user_id \
+        -object_id $package_id \
+        -privilege read
+
+    set return_array [list]
+
+    db_foreach get_n_entries {} {
+        # put the date in readable format
+        set posted_date "${entry_date} ${posted_time_pretty}"
+        array unset struct
+        set struct(content) [list -string $content]
+        set struct(postid) [list -string $entry_id]
+        set struct(userid) [list -string $user_id]
+        set struct(dateCreated) [list -date $posted_date]
+        
+        lappend return_array [list -struct [array get struct]]
+    }
+
+    return [list -array $return_array]
+}
+
+ad_proc -public blogger.deletePost {
+    appkey
+    entry_id
+    username
+    password
+    publish_p 
+} {
+    <strong>From Blogger API documentation:</strong>
+    <p>
+    Not documented anywhere on blogger.com as of 2003-10-05.
+    </p>
+
+    @param appkey (string): Ignored in OpenACS
+    @param entry_id (string): Post's entry_id
+    @param username (string): email or username for a user who has permission 
+    to post to the blog.
+    @param password (string): Password for said username/email
+    @param publish_p (boolean): Ignored.
+
+    @return boolean true if successful deletion.
+} {
+    set user_id [lars_blog_auth_for_xmlrpc \
+                     -username $username \
+                     -password $password]
+
+    lars_blogger::entry::delete -entry_id $entry_id
+
+    return [list -boolean 1]
+}
+
+
+# vinodk - The following methods are unimplemented in OpenACS
+
+#
+# blogger.getTemplate: Returns the main or archive index template of a
+# given blog.
+#
+# blogger.setTemplate: Edits the main or archive index template of a given
+# blog.
Index: openacs-4/packages/lars-blogger/tcl/metaweblog-api-init.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/tcl/metaweblog-api-init.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/lars-blogger/tcl/metaweblog-api-init.tcl	8 Dec 2003 05:55:53 -0000	1.1
@@ -0,0 +1,13 @@
+# /packages/lars-blogger/tcl/metaweblog-api-init.tcl
+ad_library {
+    Register MetaWeblog API procs
+
+    @author Vinod Kurup [vinod@kurup.com]
+    @creation-date Sun Oct  5 23:13:45 2003
+    @cvs-id $Id: metaweblog-api-init.tcl,v 1.1 2003/12/08 05:55:53 vinodk Exp $
+}
+
+xmlrpc::register_proc metaWeblog.newPost
+xmlrpc::register_proc metaWeblog.editPost
+xmlrpc::register_proc metaWeblog.getPost
+xmlrpc::register_proc metaWeblog.getRecentPosts
Index: openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs-oracle.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs-oracle.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs-oracle.xql	8 Dec 2003 05:55:53 -0000	1.1
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<queryset>
+   <rdbms><type>oracle</type><version>8.1.6</version></rdbms>
+
+<fullquery name="metaWeblog.getRecentPosts.get_n_entries">
+      <querytext>
+        select * from 
+        ( select entry_id,
+               to_char(entry_date, 'YYYY-MM-DD') as entry_date,
+               title,
+			   c.name as category,
+               content,
+               to_char(posted_date , 'HH24:MI') as posted_time_pretty
+		    from   pinds_blog_entries e, pinds_blog_categories c 
+            where  e.category_id = c.category_id(+)
+		    and    e.package_id = :package_id
+		    and    draft_p = 'f'
+		    and    deleted_p = 'f'
+		    order  by entry_date desc, posted_date desc
+        ) where rownum <= $num_posts
+      </querytext>
+</fullquery>
+
+</queryset>
Index: openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs-postgresql.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs-postgresql.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs-postgresql.xql	8 Dec 2003 05:55:53 -0000	1.1
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<queryset>
+   <rdbms><type>postgresql</type><version>7.2</version></rdbms>
+
+<fullquery name="metaWeblog.getRecentPosts.get_n_entries">
+      <querytext>
+        select entry_id,
+               to_char(entry_date, 'YYYY-MM-DD') as entry_date,
+               title,
+			   c.name as category,
+               content,
+               to_char(posted_date , 'HH24:MI') as posted_time_pretty
+		    from   pinds_blog_entries e left join pinds_blog_categories c 
+                   using (category_id)
+		    where  e.package_id = :package_id
+		    and    draft_p = 'f'
+		    and    deleted_p = 'f'
+		    order  by entry_date desc, posted_date desc
+            limit $num_posts
+      </querytext>
+</fullquery>
+
+</queryset>
Index: openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs.tcl	8 Dec 2003 05:55:53 -0000	1.1
@@ -0,0 +1,258 @@
+# /packages/lars-blogger/tcl/metaweblog-api-procs.tcl
+ad_library {
+    Support the MetaWeblog API
+    http://www.xmlrpc.com/metaWeblogApi
+
+    @author Vinod Kurup [vinod@kurup.com]
+    @creation-date Sun Oct  5 19:52:39 2003
+    @cvs-id $Id: metaweblog-api-procs.tcl,v 1.1 2003/12/08 05:55:53 vinodk Exp $
+}
+
+ad_proc -public metaWeblog.newPost {
+    package_id
+    username
+    password
+    content_array
+    publish_p
+} {
+    Create a new blog entry.
+    
+    <p>
+    The most important piece is <code>content</code> - a XML-RPC struct. Its 
+    members can be any element of the &lt;item> tag of the 
+    <a href="http://blogs.law.harvard.edu/tech/rss\#hrelementsOfLtitemgt">RSS 
+    2.0 spec </a>. 
+    As of right now, these include: title, link, description, author, 
+    category, comments, enclosure, guid, pubDate, and source. All are optional
+    except that either title or description must be provided. There is an
+    additional element which is not part of the RSS 2.0 spec - a boolean
+    named flNotOnHomePage, which, if true, specifies to post only to the
+    categories page, not to the home page. I have not yet implemented this
+    on OpenACS. Finally, since RSS 2.0 supports XML namespaces, 
+    <code>content</code> may also contain these other elements. If present,
+    these will be supplied in a substruct whose name is the namespace's URL
+    and whose subelements are the values from the namespace. This confuses me,
+    so it's not yet implemented. The spec says that the server should ignore
+    any elements it doesn't understand, so I'm covered. (grin)
+    </p>
+
+    <p>
+    <code>content</code> is supplied as an array. Most of its elements are 
+    simple key-value pairs. The enclosure element is more complex - will 
+    ignore for now. Multiple 'category' elements may be present.
+    </p>
+    
+    @param package_id The blog's package_id
+    @param username We'll determine if this is a username or an email
+    @param password password
+    @param content struct containing blog content and metadata
+    @param publish_p set to true if entry is to be published, false for draft
+
+    @return entry_id of the new post, as a string
+    @author Vinod Kurup <vinod@kurup.com>
+} {
+    array set content $content_array
+
+    set user_id [lars_blog_auth_for_xmlrpc \
+                     -username $username \
+                     -password $password]
+
+    permission::require_permission -party_id $user_id \
+        -object_id $package_id \
+        -privilege create
+
+    set entry_id [db_nextval t_acs_object_id_seq]
+
+    if { ![exists_and_not_null content(title)] } {
+        set content(title) " "
+    }
+    
+    if { ![exists_and_not_null content(description)] } {
+        set content(description) " "
+    }
+    
+    # OpenACS time format YYYY-MM-DD
+    set fmt "%Y-%m-%d"
+
+    # hopefully pubDate is in a readable format
+    if { [catch {set pubDate [clock format [clock scan $content(pubDate)] -format $fmt]}] } {
+        set pubDate [clock format [clock seconds] -format $fmt]
+    }
+    
+    # ignore 'category', 'enclosure' for now
+    
+    return [list -string [lars_blog_entry_add -entry_id $entry_id \
+                           -package_id $package_id \
+                           -title $content(title) \
+                           -content $content(description) \
+                           -content_format "text/html" \
+                           -entry_date $pubDate \
+                           -draft_p [ad_decode $publish_p 1 f t]
+                      ]]
+}
+
+ad_proc -public metaWeblog.editPost {
+    entry_id
+    username
+    password
+    content_array
+    publish_p
+} {
+    Edit blog entry.
+    
+    @see metaWeblog.newPost
+    @param entry_id entry to be edited
+    @param username We'll determine if this is a username or an email
+    @param password
+    @param content XML-RPC struct containing blog content and metadata
+    @param publish_p true for publish, false for draft
+
+    @return boolean 1 if success
+    @author Vinod Kurup <vinod@kurup.com>
+} {
+    array set content $content_array
+
+    set user_id [lars_blog_auth_for_xmlrpc \
+                     -username $username \
+                     -password $password]
+
+    permission::require_permission -party_id $user_id \
+        -object_id $entry_id \
+        -privilege write
+
+    if { ![exists_and_not_null content(title)] } {
+        set content(title) " "
+    }
+    
+    if { ![exists_and_not_null content(description)] } {
+        set content(description) " "
+    }
+    
+    # OpenACS time format YYYY-MM-DD
+    set fmt "%Y-%m-%d"
+
+    # hopefully pubDate is in a readable format
+    if { [catch {set pubDate [clock format [clock scan $content(pubDate)] -format $fmt]}] } {
+        set pubDate [clock format [clock seconds] -format $fmt]
+    }
+    
+    # ignore 'category', 'enclosure' for now
+    
+    lars_blog_entry_edit -entry_id $entry_id \
+        -title $content(title) \
+        -content $content(description) \
+        -content_format "text/html" \
+        -entry_date $pubDate \
+        -draft_p [ad_decode $publish_p 1 f t]
+    
+    return [list -boolean 1]
+}
+
+ad_proc -public metaWeblog.getPost {
+    entry_id
+    username
+    password
+} {
+    Get a blog entry.
+    
+    @param entry_id Entry to get
+    @param username We'll determine if this is a username or an email
+    @param password
+
+    @return struct containing post and metadata
+    @author Vinod Kurup <vinod@kurup.com>
+} {
+    set user_id [lars_blog_auth_for_xmlrpc \
+                     -username $username \
+                     -password $password]
+
+    permission::require_permission -party_id $user_id \
+        -object_id $entry_id \
+        -privilege read
+
+    array set content [list]
+    lars_blogger::entry::get -entry_id $entry_id -array content
+
+    set category [value_if_exists content(category_name)]
+
+    # get the package_id from the entry_id
+    set package_id [db_string package_id {}]
+
+    # get the permanent URL of this entry - use this as
+    # the link, guid and comments url
+    set perm_url "[ad_url][lars_blog_public_package_url -package_id $package_id]one-entry?[export_vars { entry_id }]"
+
+    # put the date in readable format
+    set posted_date "$content(entry_date) $content(posted_time_pretty)"
+
+    return [list -struct \
+                [list \
+                     title [list -string $content(title)] \
+                     link [list -string $perm_url] \
+                     postid [list -string $entry_id] \
+                     userid [list -string $user_id] \
+                     description [list -string $content(content)] \
+                     category [list -string $category] \
+                     comments [list -string $perm_url] \
+                     guid [list -string $perm_url] \
+                     pubDate [list -date $posted_date] \
+                     dateCreated [list -date $posted_date] \
+                    ]]
+}
+
+ad_proc -public metaWeblog.getRecentPosts {
+    package_id
+    username
+    password
+    num_posts
+} {
+    Get recent posts.
+    
+    @param package_id 
+    @param username We'll determine if this is a username or an email
+    @param password
+    @param num_posts number of posts requested
+
+    @return array of structs
+    @author Vinod Kurup <vinod@kurup.com>
+} {
+    set user_id [lars_blog_auth_for_xmlrpc \
+                     -username $username \
+                     -password $password]
+
+    permission::require_permission -party_id $user_id \
+        -object_id $package_id \
+        -privilege read
+
+    set blog_url "[ad_url][lars_blog_public_package_url -package_id $package_id]one-entry?"
+    set result ""
+
+    db_foreach get_n_entries {} {
+        set perm_url "${blog_url}[export_vars { entry_id }]"
+        
+        # put the date in readable format
+        set posted_date "${entry_date} ${posted_time_pretty}"
+
+        set struct [list -struct \
+                        [list \
+                             title [list -string $title] \
+                             link [list -string $perm_url] \
+                             postid [list -string $entry_id] \
+                             userid [list -string $user_id] \
+                             description [list -string $content] \
+                             category [list -string $category] \
+                             comments [list -string $perm_url] \
+                             guid [list -string $perm_url] \
+                             pubDate [list -date $posted_date] \
+                             dateCreated [list -date $posted_date] \
+                            ]]
+        
+        lappend result $struct
+    }
+
+    return [list -array $result]
+}
+
+# unimplemented so far
+# metaWeblog.newMediaObject 
+# metaWeblog.getCategories
Index: openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs.xql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs.xql,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/lars-blogger/tcl/metaweblog-api-procs.xql	8 Dec 2003 05:55:53 -0000	1.1
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+
+<queryset>
+
+    <fullquery name="metaWeblog.getPost.package_id">
+        <querytext>
+            select package_id from pinds_blog_entries 
+            where entry_id = :entry_id
+        </querytext>
+    </fullquery>
+
+</queryset>
Index: openacs-4/packages/lars-blogger/tcl/test/blogger-api-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/tcl/test/blogger-api-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/lars-blogger/tcl/test/blogger-api-procs.tcl	8 Dec 2003 05:56:36 -0000	1.1
@@ -0,0 +1,296 @@
+# /packages/lars-blogger/tcl/test/blogger-api-procs.tcl
+ad_library {
+     Test the Blogger API
+     @author Vinod Kurup [vinod@kurup.com]
+     @creation-date Wed Oct 15 22:39:03 2003
+     @cvs-id $Id: blogger-api-procs.tcl,v 1.1 2003/12/08 05:56:36 vinodk Exp $
+}
+
+aa_register_init_class create_blog_and_user {
+    Mount and unmount a test blog
+} {
+    # constructor
+
+    # export these vars to the environment
+    aa_export_vars {blog_id user_id username password}
+
+    # mount the blog
+    set node_name [ad_generate_random_string]
+    set blog_id [site_node::instantiate_and_mount \
+                        -node_name $node_name \
+                        -package_key lars-blogger]
+
+    # set package parameters
+    parameter::set_value \
+        -package_id $blog_id \
+        -parameter weblogs_update_ping_p \
+        -value 0
+
+    # create the user
+    set username [ad_generate_random_string]
+    set password [ad_generate_random_string]
+    set email "[ad_generate_random_string]@example.com"
+    set fn [ad_generate_random_string]
+    set ln [ad_generate_random_string]
+
+    array set user [auth::create_user \
+                        -username $username \
+                        -email $email \
+                        -first_names $fn \
+                        -last_name $ln \
+                        -password $password]
+    
+    if { [string equal $user(creation_status) ok] } {
+        set user_id $user(user_id)
+        permission::grant -party_id $user_id \
+            -object_id $blog_id \
+            -privilege create
+    } else {
+        aa_error "User creation failed. $user(creation_status) Error: $user(creation_message) $user(element_messages)"
+        set user_id ""
+    }
+} {
+    # destructor
+
+    apm_package_instance_delete $blog_id
+    acs_user::delete -permanent -user_id $user_id
+}
+
+aa_register_case -cats web -init_classes {
+    create_blog_and_user
+} blogger_new_post {
+    Make a new post.
+    Tests blogger.newPost
+} {
+    set url [ad_url][xmlrpc::url]
+    set appkey ""
+    set content "<b>[ad_generate_random_string]</b>"
+    set publish_p 1
+
+    aa_run_with_teardown -rollback -test_code {
+        set entry_id [xmlrpc::remote_call $url blogger.newPost \
+                          -string $appkey \
+                          -string $blog_id \
+                          -string $username \
+                          -string $password \
+                          -string $content \
+                          -boolean $publish_p]
+
+        aa_true "Entry $entry_id added" [string is integer $entry_id]
+
+        array set entry_info [list]
+        lars_blogger::entry::get -entry_id $entry_id -array entry_info
+
+        aa_equals "Entry content is correct" $entry_info(content) $content
+    }
+}
+
+aa_register_case -cats web -init_classes {
+    create_blog_and_user
+} blogger_edit_post {
+    Edit a post.
+    Tests blogger.editPost
+} {
+    set url [ad_url][xmlrpc::url]
+    set appkey ""
+    set content_1 "[ad_generate_random_string]"
+    set content_2 "[ad_generate_random_string]"
+    set publish_p 1
+
+    aa_run_with_teardown -rollback -test_code {
+        set entry_id [xmlrpc::remote_call $url blogger.newPost \
+                          -string $appkey \
+                          -string $blog_id \
+                          -string $username \
+                          -string $password \
+                          -string $content_1 \
+                          -boolean $publish_p]
+
+        aa_true "Entry $entry_id added" [string is integer $entry_id]
+
+        xmlrpc::remote_call $url blogger.editPost \
+             -string $appkey \
+             -string $entry_id \
+             -string $username \
+             -string $password \
+             -string $content_2 \
+             -boolean $publish_p
+             
+        array set entry_info [list]
+        lars_blogger::entry::get -entry_id $entry_id -array entry_info
+
+        aa_equals "Edited content is correct" $entry_info(content) $content_2 
+    }
+}
+
+aa_register_case -cats web -init_classes {
+    create_blog_and_user
+} blogger_list_user_blogs {
+    List blogs to which this user can post.
+    Tests blogger.getUsersBlogs
+} {
+    set url [ad_url][xmlrpc::url]
+    set appkey ""
+
+    aa_run_with_teardown -rollback -test_code {
+        set blog_list [xmlrpc::remote_call $url blogger.getUsersBlogs \
+                          -string $appkey \
+                          -string $username \
+                          -string $password]
+
+        # Get the first blog in the list
+        array set one_blog [lindex $blog_list 0]
+
+        aa_equals "blog_id $blog_id returned" $one_blog(blogid) $blog_id
+
+        # TODO - test multiple blogs with different permissions
+    }
+}
+
+aa_register_case -cats web -init_classes {
+    create_blog_and_user
+} blogger_get_user_info {
+    Get user info.
+    Test blogger.getUserInfo
+} {
+    set url [ad_url][xmlrpc::url]
+    set appkey ""
+
+    aa_run_with_teardown -rollback -test_code {
+        array set user_info [xmlrpc::remote_call $url blogger.getUserInfo \
+                          -string $appkey \
+                          -string $username \
+                          -string $password]
+
+        aa_equals "user_id correct" $user_info(userid) $user_id
+    }
+}
+
+aa_register_case -cats web -init_classes {
+    create_blog_and_user
+} blogger_get_post {
+    Get a single post.
+    Test blogger.getPost
+} {
+    set url [ad_url][xmlrpc::url]
+    set appkey ""
+    set content "<b>[ad_generate_random_string]</b>"
+    set publish_p 1
+
+    aa_run_with_teardown -rollback -test_code {
+        set entry_id [xmlrpc::remote_call $url blogger.newPost \
+                          -string $appkey \
+                          -string $blog_id \
+                          -string $username \
+                          -string $password \
+                          -string $content \
+                          -boolean $publish_p]
+
+        array set entry [xmlrpc::remote_call $url blogger.getPost \
+                             -string $appkey \
+                             -string $entry_id \
+                             -string $username \
+                             -string $password]
+
+        aa_equals "content correct" $entry(content) $content
+        aa_equals "user_id correct" $entry(userid) $user_id
+        aa_equals "entry_id correct" $entry(postid) $entry_id
+    }
+}
+
+aa_register_case -cats web -init_classes {
+    create_blog_and_user
+} blogger_get_recent_posts {
+    Get recent posts.
+    Tests blogger.getRecentPosts
+} {
+    set url [ad_url][xmlrpc::url]
+    set appkey ""
+    set content1 "<b>[ad_generate_random_string]</b>"
+    set content2 "<b>[ad_generate_random_string]</b>"
+    set publish_p 1
+
+    aa_run_with_teardown -rollback -test_code {
+        set entry1_id [xmlrpc::remote_call $url blogger.newPost \
+                          -string $appkey \
+                          -string $blog_id \
+                          -string $username \
+                          -string $password \
+                          -string $content1 \
+                          -boolean $publish_p]
+
+        set entry2_id [xmlrpc::remote_call $url blogger.newPost \
+                          -string $appkey \
+                          -string $blog_id \
+                          -string $username \
+                          -string $password \
+                          -string $content2 \
+                          -boolean $publish_p]
+
+        # get the entries
+        set entry_list [xmlrpc::remote_call $url blogger.getRecentPosts \
+                            -string $appkey \
+                            -string $blog_id \
+                            -string $username \
+                            -string $password \
+                            -int 2]
+
+        # newest posts are returned first
+        array set entry2 [lindex $entry_list 0]
+        array set entry1 [lindex $entry_list 1]
+
+        aa_equals "content 2 correct" $entry2(content) $content2
+        aa_equals "entry_id 2 correct" $entry2(postid) $entry2_id
+
+        aa_equals "content 1 correct" $entry1(content) $content1
+        aa_equals "entry_id 1 correct" $entry1(postid) $entry1_id
+    }
+}
+
+aa_register_case -cats web -init_classes {
+    create_blog_and_user
+} blogger_delete_post {
+    Delete post.
+    Tests blogger.deletePost
+} {
+    set url [ad_url][xmlrpc::url]
+    set appkey ""
+    set content "<b>[ad_generate_random_string]</b>"
+    set publish_p 1
+
+    aa_run_with_teardown -rollback -test_code {
+        set entry_id [xmlrpc::remote_call $url blogger.newPost \
+                          -string $appkey \
+                          -string $blog_id \
+                          -string $username \
+                          -string $password \
+                          -string $content \
+                          -boolean $publish_p]
+
+        array set entry [xmlrpc::remote_call $url blogger.getPost \
+                             -string $appkey \
+                             -string $entry_id \
+                             -string $username \
+                             -string $password]
+
+        aa_equals "entry_id correct" $entry(postid) $entry_id
+        array unset entry
+
+        set result [xmlrpc::remote_call $url blogger.deletePost \
+                            -string $appkey \
+                            -string $entry_id \
+                            -string $username \
+                            -string $password \
+                            -boolean $publish_p]
+
+        aa_true "delete succeeded" $result
+
+        array set entry [xmlrpc::remote_call $url blogger.getPost \
+                             -string $appkey \
+                             -string $entry_id \
+                             -string $username \
+                             -string $password]
+
+        aa_false "entry_id gone" [info exists entry(postid)]
+    }
+}
Index: openacs-4/packages/lars-blogger/tcl/test/metaweblog-api-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/tcl/test/metaweblog-api-procs.tcl,v
diff -u
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/lars-blogger/tcl/test/metaweblog-api-procs.tcl	8 Dec 2003 05:56:36 -0000	1.1
@@ -0,0 +1,99 @@
+# /packages/lars-blogger/tcl/test/metaweblog-api-procs.tcl
+ad_library {
+     Test the MetaWeblog API
+     @author Vinod Kurup [vinod@kurup.com]
+     @creation-date Sun Nov 30 22:15:14 2003
+     @cvs-id $Id: metaweblog-api-procs.tcl,v 1.1 2003/12/08 05:56:36 vinodk Exp $
+}
+
+aa_register_case -cats web -init_classes {
+    create_blog_and_user
+} mw_new_get_edit_post {
+    Test new, edit and get post functions. 
+    metaWeblog.newPost, metaWeblog.editPost, metaWeblog.getPost, metaWeblog.getRecentPosts
+} {
+    set url [ad_url][xmlrpc::url]
+
+    set orig_content_text "<b>[ad_generate_random_string]</b>"
+    set new_content_text "<b>[ad_generate_random_string]</b>"
+
+    # put them into arrays
+    set orig_content(description) [list -string $orig_content_text]
+    set new_content(description) [list -string $new_content_text]
+
+    set publish_p 1
+
+    aa_run_with_teardown -rollback -test_code {
+        # create an entry
+        set entry_id [xmlrpc::remote_call $url metaWeblog.newPost \
+                          -string $blog_id \
+                          -string $username \
+                          -string $password \
+                          -struct [array get orig_content] \
+                          -boolean $publish_p]
+
+        aa_true "New entry added successfully" [string is integer $entry_id]
+        
+        # Test entry via normal API
+        array set new_entry [list]
+        lars_blogger::entry::get -entry_id $entry_id -array new_entry
+
+        aa_equals "New entry content correct" \
+            $new_entry(content) $orig_content_text
+
+        # Test entry via MetaWeblog API
+        array set get_entry [xmlrpc::remote_call $url metaWeblog.getPost \
+                                 -string $entry_id \
+                                 -string $username \
+                                 -string $password]
+
+        aa_equals "Content correct via getPost" \
+            $get_entry(description) $orig_content_text
+
+        # Edit the entry
+        xmlrpc::remote_call $url metaWeblog.editPost \
+            -string $entry_id \
+            -string $username \
+            -string $password \
+            -struct [array get new_content] \
+            -boolean $publish_p
+            
+        array set edited_entry [list]
+        lars_blogger::entry::get -entry_id $entry_id -array edited_entry
+
+        aa_equals "Edited content is correct" \
+            $edited_entry(content) $new_content_text
+
+        # Add 2 posts and then get them via getRecentPosts
+
+        set content1_text [ad_generate_random_string]
+        set content1(description) [list -string $content1_text]
+        set content2_text [ad_generate_random_string]
+        set content2(description) [list -string $content2_text]
+
+        set entry1_id [xmlrpc::remote_call $url metaWeblog.newPost \
+                           -string $blog_id \
+                           -string $username \
+                           -string $password \
+                           -struct [array get content1] \
+                           -boolean $publish_p]
+
+        set entry2_id [xmlrpc::remote_call $url metaWeblog.newPost \
+                           -string $blog_id \
+                           -string $username \
+                           -string $password \
+                           -struct [array get content2] \
+                           -boolean $publish_p]
+
+        set entry_list [xmlrpc::remote_call $url metaWeblog.getRecentPosts \
+                            -string $blog_id \
+                            -string $username \
+                            -string $password \
+                            -int 2]
+
+        array set result2 [lindex $entry_list 0]
+        aa_equals "Get most recent post" $result2(description) $content2_text
+        array set result1 [lindex $entry_list 1]
+        aa_equals "Get 2nd most recent post" $result1(description) $content1_text
+    }
+}
Index: openacs-4/packages/lars-blogger/www/doc/index.html
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/lars-blogger/www/doc/index.html,v
diff -u -r1.5 -r1.6
--- openacs-4/packages/lars-blogger/www/doc/index.html	16 Oct 2003 23:10:46 -0000	1.5
+++ openacs-4/packages/lars-blogger/www/doc/index.html	8 Dec 2003 05:55:53 -0000	1.6
@@ -146,6 +146,17 @@
   parameters.
 </p>
 
+<h3>XML-RPC APIs</h3>
+
+<p>
+  The Blogger and MetaWeblog APIs are supported. An <a
+  href="http://archipelago.phrasewise.com/rsd">RSD</a> link is placed on
+  your Blog's front page, allowing XML-RPC client tools to automatically
+  discover which API's they can use. See the <a href="/doc/xml-rpc">XML-RPC
+  package documentation</a> for more details. Disable the XML-RPC server to
+  disallow access to your blog via XML-RPC.
+</p>
+
 <h3>Road Map</h3>
 
 <ul>
@@ -157,7 +168,6 @@
   <li>Ability to modify templates for each instance individually through
     the UI. </li>
   <li>Support for Trackback</li>
-  <li>Support for the Blogger and MetaWeblog APIs</li>
   <li>WYISIYG editor</li>
   <li>Improved RSS feed</li>
   <li>Improved Configuration Settings.</li>