<?xml version="1.0"?>
<rss version="2.0">
  <channel>
    <title>A Wiki</title>
    <link>http://gadgets.xml-comma.org/</link>
    <description><![CDATA[All public posts]]></description>
    <language>en-us</language>
    <pubDate>21 Oct 2008 11:16:44 GMT</pubDate>
    <lastBuildDate>21 Oct 2008 11:16:44 GMT</lastBuildDate>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>Gadgets</generator>
<item>
<title>Gadgets::Standard::Post.pm</title>
<link>http://gadgets.xml-comma.org/post/main/07zQcRb7yOMpJu07</link>
<author>dug@allafrica.com (Douglas Hunter)</author>
<description><![CDATA[#NAME
    Gadgets::Standars::Post - Post creation and management

#METHODS

##my $post = Gadgets::Standard::Post->new ( [parent_key => doc_key] )
      my $reply = Gadgets::Standars::Post->new( parent_key => $parent->doc_key );

    Creates a new Post doc and returns it. Takes optional named paramater
    "parent_key", which is the doc_key of the parent of this post (for
    replies)

##my $post = Gadgets::Standard::Post->get_by_title( "title" )
      my $post = Gadgets::Standars::Post->get_by_title( "Wiki Documentation" );

    Returns most recently created Post titled "title", or undef.

##my $post = Gadgets::Standard::Post->get_by_id ( "doc_id" )
      my $post = Gadgets::Standars::Post->get_by_id( "1234adsf" );

    Returns Post of id "doc_id", or undef.

##my $post = Gadgets::Standard::Post->get_by_key ( "doc_key" )
      my $post = Gadgets::Standars::Post->get_by_key( "Post|main|1234asfd" );

    Returns Post of key "doc_key", or undef.

##my $it = Gadgets::Standard::Post->iterator( %args )
      my $it = Gadgets::Standars::Post->iterator( %iter_args );

    Convenience wrapper for

      XML::Comma::Def->Post
                     ->get_index("main")
                     ->iterator ( %iter_args )

##my @kids = Gadgets::Standard::Post->get_descendant_keys( "doc_key" )
      my @descendants = 
        Gadgets::Standars::Post->get_descendant_keys( $post->doc_key );

    returns a list of descendants of doc_key

##$post->world_readable
      $post->world_readable  and  show();

    returns true if the post is world readable, false otherwise.

##$post->check_authz ( $user, "perm_type" );
    Where "perm_type" is "read", "write" or "post"

      my $can_read = $post->check_authz( $user, "read" );

##$post->top_parent
    my $originator = $self->top_parent;

    returns the top parent doc object, or $self if we have no ancestors.

##$post->top_parent_key
    my $originator = $self->top_parent_key;

    returns the top parent doc_key, or $self->doc_key if we have no
    ancestors.



]]></description>
<pubDate>21 Oct 2008 11:16:44 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/07zQcRb7yOMpJu07</guid>
</item>
<item>
<title>Gadgets::AuthManager.pm</title>
<link>http://gadgets.xml-comma.org/post/main/07zQZyW2nR4Zy6yM</link>
<author>dug@allafrica.com (Douglas Hunter)</author>
<description><![CDATA[#NAME
    Gadgets::AuthManager - Authenticate and authorize. Flexibly.

#METHODS

##$g->auth->check_authn ( [ $extra_login_params ] )
    check_authn functions similarly to force_authn, except that it only
    returns a user object if the user has already logged on, or undef if
    there is no session authentication. It does NOT redirect to a login page
    like force_authn.

##$g->auth->force_authn ( [ $extra_login_params ] )
    Usually, force_authn is called by system core code (admin_acl_authz, for
    example). But you can call it from the init block of a mason component,
    as well, to force authentication for a given resource. Called in this
    way, the login system cannot handle the passing of POST arguments, but
    everything else works. Handling POST args (again, an earlier version of
    AuthManager did so) should be on the todo list.

##$g->auth->login_init
    Login components are often self-submitting, so that login failures can
    be reported and a login page redisplayed. This method is called at the
    beginning of a login component's init block. If this is a login
    submission, and it's successful, a redirect (external redirect for GET,
    internal redirect for POST) will take the user to the page that was
    originally requested. Otherwise a list of values to be filed into
    various login-form elements will be returned.

##$g->auth->logout ( $optional_redirect )
    Call to log a user out. Always redirects (using $m->redirect) after
    performing whatever logout and cleanup is necessary. With no argument,
    redirects to the url in the HTTP Referer header. If passed an argument,
    uses that string as a target url for the redirect.

##$g->auth->new_user
    Call to creates a new user object and underlying data structure.

##$g->auth->new_user_login
    Call to create -- and immediately log in -- a new user.

##$g->auth->authenticate
    Authenticate "in a vacuum," passing login information through to the
    Authner object. This routine would not normally be called directly, but
    it's here in the API for unusual usages.

##$g->auth->refresh_authn
    Call to refresh a user's authentication. This means that any timeout in
    the authn credential is set anew, extending the authn credential's
    validity.

##$g->auth->refresh_changed_authn
    Call to refresh a user's authentication after user information been
    changed in some underlying way. For example, because UserFactory caches
    the display_name string in a client-side cookie, if you change a user's
    display_name without calling this method, the new name will probably not
    "show up" immediately for Mason-side code.

    Here is a common use of refresh_changed_authn:

      $user->set_display_name ( @args_the_def_expects );
      $self->refresh_changed_authn();

      Your profile has been changed. You are logged in
      as: <% $user->display_name %>.


]]></description>
<pubDate>21 Oct 2008 11:13:02 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/07zQZyW2nR4Zy6yM</guid>
</item>
<item>
<title>Gadgets API</title>
<link>http://gadgets.xml-comma.org/post/main/07zQXqP63s22iNR-</link>
<author>dug@allafrica.com (Douglas Hunter)</author>
<description><![CDATA[[[titled_link( "Gadgets.pm" )]]

[[titled_link( "Gadgets::User.pm" )]]

[[titled_link( "Gadgets::Group.pm" )]]

[[titled_link( "Gadgets::AuthManager.pm" )]]

[[titled_link( "Gadgets::Standard::Post.pm" )]]

]]></description>
<pubDate>21 Oct 2008 11:10:46 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/07zQXqP63s22iNR-</guid>
</item>
<item>
<title>Gadgets::Group.pm</title>
<link>http://gadgets.xml-comma.org/post/main/07zQXdaTNtqZWA-G</link>
<author>dug@allafrica.com (Douglas Hunter)</author>
<description><![CDATA[#NAME
    Gadgets::Group - Group creation and management

#METHODS

##my $group = Gadgets::Group->new_group ( group_name => "My Group" )
    Creates a new group of "group_name". Dies if a group of that name
    already exists. Returns the new GadgetsGroup doc object upon success.

##my $group = Gadgets::Group->get_by_name ( "My Group" )
    Returns either a GadgetsGroup document or undef.

##my $group = Gadgets::Group->get_by_id ( group_id )
    Returns either a GadgetsGroup document or undef.

##my $group = Gadgets::Group->get_by_key ( group_key )
    Returns either a GadgetsGroup document or undef.

##my @groups = Gadgets::Group->get_memberships_recursively( user_key | group_key ... )
    takes one or more GadgetsUser or GadgetsGroup doc_keys, returns the
    inclusive list of group memberships. Usually used to get the list of
    groups a user belongs to, like so:

      my @groups = Gadgets::Group->get_memberships_recursively
                                     ( $user->doc_key );


]]></description>
<pubDate>21 Oct 2008 11:10:33 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/07zQXdaTNtqZWA-G</guid>
</item>
<item>
<title>Gadgets::User.pm</title>
<link>http://gadgets.xml-comma.org/post/main/07zQVtEBu9WxfCoB</link>
<author>dug@allafrica.com (Douglas Hunter)</author>
<description><![CDATA[#NAME
    Gadgets::User - User creation and management

#METHODS

##my $user = Gadgets::User->new_user ( email => 'my@email.addr', password => "s33cr3t" )
    Creates and returns a new GadgetsUser document. Ensures email
    uniqueness, throwing an exception upon failure.

##my $user = Gadgets::User->get_by_email( 'my@email.addr' )
    returns GadgetsUser doc on success, undef on failure.

##my $user = Gadgets::User->get_by_key ( user_key )
    Returns either a GadgetsUser document or undef.

##my $user = Gadgets::User->get_by_id ( user_id )
    Returns either a GadgetsUser document or undef.

##$user->belongs_within( group_key ... )
    Returns true if the user belongs within the group(s), false otherwise.


]]></description>
<pubDate>21 Oct 2008 11:08:41 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/07zQVtEBu9WxfCoB</guid>
</item>
<item>
<title>Gadgets.pm</title>
<link>http://gadgets.xml-comma.org/post/main/07zQTXM7S2kG2hnv</link>
<author>dug@allafrica.com (Douglas Hunter)</author>
<description><![CDATA[
#NAME
    Gadgets - Go Go Gadgets!

#DESCRIPTION
    Gadgets is a framework built out of a suite of Perl modules that makes
    gluing HTML::Mason code and XML::Comma easy and fun.

    Gadgets.pm is the entry point for Gadgets, providing the global $g
    object. Contained in $g are objects of several flavors, including an
    Authentication class, a Configuration class, and a Resolver class. See
    the SEE ALSO section below for links to those modules.

#METHODS

##simple_handle_request
    Basic (and usually adequate) method to call from handler.pl to handle
    each Apache request. See APAHCE CONFIGURATION, below.

##implements ( api  => <api_name>, [ path => <request path> ] )
    Look up which installed Gadget or Gadgets "implement" a particular API.
    This method is used by the resolver to find components using the special
    Gadget ampersand call syntax:

      $m->comp ( '/&Test/foo' );
      <& '/&Test/foo' &>

    When passed a path argument, this method returns a single par_file name
    (or undef, if no installed Gadget has declared that it implements the
    requested API). If multiple Gadgets are installed, the conf system is
    checked for an indication of which Gadget is preferred for the supplied
    path. If no relevant conf value is found, the Gadget with the oldest
    install_time value is returned.

    When not passed a path argument, this method returns a list of installed
    Gadgets that implement the API.

    By convention, interface names are in CamelCase, so that Mason calls to
    interface-defined components are easier to read.

    Example conf keys:

      PATH           KEY                 VALUE             GADGET
      /              'FourColorSkin'  'Sunset Skin'      core_impl
      /foo_blog      'FourColorSkin'  'Dark Sky Skin'    core_impl

##auth
    returns the underlying Gadgets::AuthManager object. See
    Gadgets::AuthManager for the "auth" methods.

##conf
    returns the underlying Gadgets::Configuration object. See
    Gadgets::Configuration for the "conf" methods.

#APACHE CONFIGURATION
    Two levels of configuration are required: a few lines in Apache's
    httpd.conf file (or equivalent), and a Mason handler.pl file.

    In httpd.conf:

      PerlModule       Gadgets::ParResolver;
      PerlTransHandler Gadgets::ParResolver::trans_handler

    and something like the following, to have Mason handle part (or all) of
    Apache's location space:

    <Location /> SetHandler perl-script PerlHandler HTML::Mason </Location>
    PerlRequire /web-working/handler.pl

    Here is a complete handler.pl example:

      package HTML::Mason;

      use HTML::Mason;
      use Apache::Constants qw(:common);
      use Apache::Request;

      use strict;

      {
        package HTML::Mason::Commands;
        use vars qw( $g );

        use XML::Comma;
        use Gadgets;

        $g = Gadgets->new
          ( verbose        => 0,
            comp_root      => '/web-working',
            resolver_class => 'Gadgets::Resolver',
            data_dir       => '/usr/local/apache/mason_data',
            error_mode     => 'output', );
      }

      sub handler {
        my ($r) = @_;

        return -1  if  $r->uri() =~ m|^/static/|;
        my $status;

        $status = $HTML::Mason::Commands::g->simple_handle_request ( $r );
        return $status;
      }

      1;

    You may have noticed the "simple_handle_request" method, used in our
    handler sub above. Here is the code for that method, in its entirety:

      sub simple_handle_request {
        my ( $self, $r ) = @_;

        return DECLINED  if  $r->content_type  and
                             $r->content_type =~ m|^httpd|;

        if ( $r->content_type                 and
             $r->content_type  !~  m|^text| ) {
          if ( $r->pnotes('PAR') ) {
            return $self->{resolver}->send_raw_file ( $r );
          } else {
            return DECLINED;
          }
        }

        return $self->{apache_handler}->handle_request ( $r );
      }

    Many handler.pl setups have complex setups to determine whether (and
    how) Mason should serve top-level requests. If you need to integrate the
    Gadgets resolver into such a setup, you'll need to code your own version
    of the logic above.

    It's important to avoid asking the Gadgets resolver to handle "httpd/*"
    content types. Apache uses some heavy wizardry under the covers to make
    requests for directories to eventually turn into requests for index.html
    files. (And along the way pick up missing trailing slashes.) We're not
    going to be able to do this as well as Apache, so we needs to get out of
    its way as much as possible.

    It's also worth noting that Gadgets will often include binary files that
    need to be served without the benefit of Mason componentization. The
    "simple_handle_request" routine assumes that all "text/*" content types
    are fair game for Mason, but that all other content types will be sent
    byte-for-byte to the client. Your rules for this may differ.

#DEMO
    There is a misc/demo directory in the distribution, with a demo Gadget
    par file and gadget spec.




]]></description>
<pubDate>21 Oct 2008 11:06:10 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/07zQTXM7S2kG2hnv</guid>
</item>
<item>
<title>Gadgets HTTP POST API</title>
<link>http://gadgets.xml-comma.org/post/main/07zQDfNFrgi-Ea47</link>
<author>dug@allafrica.com (Douglas Hunter)</author>
<description><![CDATA[# POST API

In subversion, there is a new file lib/Gadgets/Standard/Post/HTTP.pm.  It
implements a mod_perl handler implementing a Gadget Post Service that can be
configured using an Apache Location directive, like so:

    <Location "/post/service/">
      SetHandler perl-script
      PerlHandler Gadgets::Standard::Post::HTTP
    </Location>

The rest of this document assumes that the service is located at
/post/service/.

The service accepts GET requests (to read a post), POST requests (to make a
new post or reply to an existing post), and PUT requests (to edit an existing
post).


##Required Headers

A valid username and password is required for all requests to the service.
They are sent in the X-Gadgets-Post-Username and X-Gadgets-Post-Password
headers, respectively.  If there is an error authenticating, the service
returns a AUTH_REQUIRED status code and sets a message in the 
X-Gadgets-Post-Authenticate-Error header in the response.


##Reading Posts

GET requests need a doc key in the url, like so: 
http://wiki.oblong.net/post/service/Post|main|07NGOqmsS-Yx2OMN.  If the post
is not found, the status code NOT_FOUND is returned.  If the authenticated
user doesn't have permission to view the post, a FORBIDDEN response is
returned.  Assuming that the post exists and the authenticated user has
permission to view the post, the following headers will be included in the
response:

<pre><code>
X-Gadgets-Post-Doc-Key [the doc key]
X-Gadgets-Post-Title [the title]
X-Gadgets-Post-Tags [a comma separated list of the tags]
X-Gadgets-Post-Owner [the doc key of the owner of the post]
X-Gadgets-Post-Perms-Post [a comma separated list of User/Group doc keys]
X-Gadgets-Post-Perms-Write [a comma separated list of User/Group doc keys] 
X-Gadgets-Post-Perms-Read [a comma separated list of User/Group doc keys]
</code></pre>

The body of the response will contain the body of post.


##Creating New Posts

*note* Currently, only the "application/x-www-form-urlencoded" content
type is supported for POST and PUT.  It is the client authors
responsibility to set the content-type header, as well as URI encode
the body of the POST or PUT.

POST requests must not include a doc key, the post directly to the service,
like so:  http://wiki.oblong.net/post/service/.  The following headers may be
included in the request to the service:

<pre><code>
X-Gadgets-Post-Title [the title]
X-Gadgets-Post-Tags [a comma separated list of the tags]
X-Gadgets-Post-Perms-Post [a comma separated list of User/Group doc keys]
X-Gadgets-Post-Perms-Write [a comma separated list of User/Group doc keys] 
X-Gadgets-Post-Perms-Read [a comma separated list of User/Group doc keys]
X-Gadgets-Post-Is-Draft [a true value makes the post a draft]
X-Gadgets-Post-Is-Reply-To [the doc key of the post this is a reply to]
</code></pre>

The body of the request becomes the body of the post that is created by the
request.  The response of the request is same as if a GET was requested to the
post created.


##Editing Existing Posts

PUT requests need a doc key in the url, like so: 
http://wiki.oblong.net/post/service/Post|main|07NGOqmsS-Yx2OMN.  If the post
is not found, the status code NOT_FOUND is returned.  If the authenticated
user doesn't have permission to view the post, a FORBIDDEN response is
returned.  

The following headers may be included in the request to the service:


<pre><code>
X-Gadgets-Post-Title [the title]
X-Gadgets-Post-Tags [a comma separated list of the tags]
X-Gadgets-Post-Perms-Post [a comma separated list of User/Group doc keys]
X-Gadgets-Post-Perms-Write [a comma separated list of User/Group doc keys] 
X-Gadgets-Post-Perms-Read [a comma separated list of User/Group doc keys]
</code></pre>

The body of the request becomes the body of the post that is being edited by 
the request.  The response of the request is same as if a GET was requested to 
the post edited.


##Headers Reference

<pre><code>
X-Gadgets-Post-Username
X-Gadgets-Post-Password
X-Gadgets-Post-Authenticate-Error

X-Gadgets-Post-Is-Reply-To
X-Gadgets-Post-Title
X-Gadgets-Post-Tags

X-Gadgets-Post-Perms-Post
X-Gadgets-Post-Perms-Write
X-Gadgets-Post-Perms-Read
X-Gadgets-Post-Perms-Error

X-Gadgets-Post-Is-Draft
X-Gadgets-Post-No-Store

X-Gadgets-Post-Doc-Key
X-Gadgets-Post-Owner
</code></pre>

<pre><code>
</code></pre>

Attached is a Perl script that exercises the POST API.]]></description>
<pubDate>21 Oct 2008 10:49:15 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/07zQDfNFrgi-Ea47</guid>
<enclosure url="http://gadgets.xml-comma.org/file/main/07zQDfNFrgi-Ea47/e7PGX5wm" length="2050" type="text/x-perl" />

</item>
<item>
<title>New Gadget Tutorial</title>
<link>http://gadgets.xml-comma.org/post/main/07zPdzUR8L3BfCYW</link>
<author>dug@allafrica.com (Douglas Hunter)</author>
<description><![CDATA[## Writing a new gadget

As mentioned before, a Gadgets installation is made up of one or more
applications bundled as Par files.  Sometimes we refer to an
individual application as a gadget.  A Gadgets installation typically
employes several of these (commonly the Wiki or Blog for high-level
functionality, Post for the actual document creation/edit interface,
and Template for skinning the application).

Once we're up and running, to make a new gadget we change into our
Gadgets checkout directory and run
`./misc/admin/create_devel_gadget.pl`.

And here is the output of create_devel_gadget.pl --help

<pre><code>
  ./misc/admin/create_devel_gadget.pl --name "Gadget-Name"
     --implements "APIName" [ --implements "OtherAPIName" ]
     --uri_path "/uri/to/dispatch/"
     --comp_path "/path/to/mason/components/"
     [ --lib "/path/to/perl/lib/" --lib "/other/path/to/perl/lib/" ]
     [ --def "/path/to/comma/def" --def "/other/path/to/comma/def" ]

  OPTIONS:

    --name

      The name of the gadget you are creating.  This is required, and
      should be representative of the functionality the gadget
      provides.

    --implements

      The name(s) of the API(s) that this gadget implements.  If
      nothing is specified, it will default to the name of the gadget.
      Multiple APIs may be specified, if your gadget implements them.

    --uri_path

      The uri that will dispatch requests to this gadget.  This is
      required, and should be unique to this gadget.

    --comp_path

      The path to your development HTML::Mason components.  This is
      required, and the directory specified must be relative to the
      HTML::Mason component_root specified in your handler.pl (or
      otherwise specified in your Apache configuration).  For
      instance, if your component_root is /mycorp/webtree/, and your
      development components are in the directory
      /mycorp/webtree/kung-fu/, your would specify "/kung-fu/" as your
      comp_path.

    --lib

      The path(s) to a directory(s) containing any perl modules that will be
      bundled with this gadget.  This is typically used for bundling
      gadget specific logic that doesn't belong in the Gadgets
      standard library.

   --def

     The path(s) any XML::Comma document defition(s) that this gadget
     will use.  This is typically used for bundling a gadget specific
     doctype that doesn't belong in the Gadgets standard library.
</code></pre>

Lets start by making a toy application called that lets folks write
book reviews and then have discussions about those reviews.

We'll name this application "Jonas", after Jonas Wright.  Jonas was
flayed alive on August 4, 1632.  His skin was then used to re-bind
what was once his most favored posession, a book.

first, our project directory:

`mkdir ~/prj/gadgets/Jonas`

Then our "comma" directory.  This is where Comma Document Definitions
belong.

`mkdir ~/prj/gadgets/Jonas/comma`

"lib" for our Perl libraries:

`mkdir ~/prj/gadgets/Jonas/lib`

And finally "mason" for our HTML::Mason templates.

`mkdir ~/prj/gadgets/Jonas/mason`

And now we'll make a symlink of our project's HTML::Mason template
directory into our component_root, at the path that I'll use later for
my argument to --uri_path.  Since I keep my component root down
"/webtree":

`ln -s ~/prj/gadgets/Jonas/mason /webtree/jonas`

And make a stub for the Perl module used by Jonas by pasting these contents:

<pre><code>
package Jonas;

use warnings;
use strict;

1;
</code></pre>

to ~/prj/gadgets/Jonas/lib/Jonas.pm.  This is where we'll
refactor commonly used code from our Mason templates into.

Our application will need to store some meta-data about books being
reviewed, so we'll also write a simple Comma Document Definition to
define that metadata, and how it is stored by pasting these contents:

    <DocumentDefinition>
    <name>Review</name>

    <element><name>post_key</name></element>

    <element><name>title</name></element>
    <element><name>ISBN</name></element>
    <element><name>author</name></element>
    <element><name>publisher</name></element>
     
    <element>
      <name>rating</name>
      <macro>enum: 1 .. 5</macro>
    </element>
    
    <required> qw( post_key title ISBN author publisher rating ) </required>
  
    <store>
      <name>main</name>
      <base>review</base>
      <location>GMT_3layer_dir</location>    
      <location>Sequential_file:'extension','.review'</location>
      <file_permissions>0664</file_permissions>
      <index_on_store>main</index_on_store>    
    </store>

    <index>
      <name>main</name>
      <field><name>post_key</name></field>
      <field><name>title</name></field>
      <field><name>ISBN</name></field>
      <field><name>author</name></field>
      <field><name>publisher</name></field>
      <field><name>rating</name></field>
    </index>

    </DocumentDefinition>

to ~/prj/gadgets/Jonas/defs/Review.def


 Now that we have everything stubbed out, we run create_devel_gadget.pl with the following arguments:

<pre><code>
./misc/admin/create_devel_gadget.pl --name Jonas \
                                      --uri_path "/review/" \
                                      --comp_path "/jonas/" \
                                      --lib /home/username/prj/gadgets/Jonas/lib/ \
                                      --def /home/dug/username/gadgets/Jonas/defs/Review.def
</pre></code>

 If everything has gone well so far, we should be able to `echo "hello
 world!" >> /ob/webtree/jonas/test.html`, load up
 http://localhost/review/ in a browser, and see our handiwork!
 (test.html can be deleted after verifying things are working).

 We're going to keep this application very simple.  There will be a
 form to create new reviews, and a page to list existing reviews.

 Let's hack in these basic features.  We'll start with our navigation
 and url dispatch.  By creating `/ob/webtree/jonas/autohandler` that
 looks like this:


    <%init>
      my $base = $g->interface_path( api => "Jonas" );
    </%init>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html>
    <head>
    <title>Jonas</title>
    </head>

    <div class="nav">
      <a href="<% $base . "new" %>">Write a Review</a> | <a href="<% $base . "list" %>">Read Reviews</a> </div>

    % $m->call_next( %ARGS );

    </html>


we add our html declaration and basic navigation to every page.  Autohandler is a special component name in mason that says "execute me every request down this path".

Notice that <%init> block.  We asked the gadgets API for the
interface_path for "Jonas".  That coresponds to the "--uri-path"
argument we passed to create_devel_gadget.pl, and by using this path
we make our application relocatable.

And now a basic `index.html` page:

    <div class="welcome">
    Welcome to Jonas, the book review gadget.  Why is this site called <a href="http://www.google.com/search?hl=en&q=%22Jonas+Wright%22+book+binding">Jonas</a>?
    </div>

which automatically gets wrapped up by our autohandler (like all other
components).  This is the only component that isn't wrapped up by our
URI dispatch component, the dhandler (show next).  Calls to
"http://localhost/review/" or "http://localhost/review/index.html"
skip the dhandler (which only handles virtual requests) and directly
call "index.html".

Next, we'll write a `dhandler`.  Dhandlers in Mason handle virtual
requests, and are a nice and easy way to do URI dispatching.  We set up our URI dispatch by adding making a `/ob/webtree/jonas/dhandler` like so:

    <%doc>
      /new     -> edit
      /edit/id -> edit?doc_id=id
      /read    -> list
      /read/id -> list?doc_id=id
    </%doc>
    %
    <%once>
      use Jonas;
    </%once>
    %
    <%perl>

      my $path = $m->dhandler_arg;

      if ( $path eq "new" ) {
        $m->comp( "edit" );
      } elsif ( $path =~ /^edit\/(.+)/ ) {
        my $id = $1;
        my $review = Jonas->get_review_by_id( $id );
        $review  or  $m->clear_and_abort( 404 );
        $m->comp( "edit", review_doc => $review );
      } elsif ( $path eq "read" ) {
        $m->comp( "list" );
      } elsif ( $path =~ /^read\/(.+)/ ) {
        my $id = $1;
        my $review = Jonas->get_review_by_id( $id );
        $review  or  $m->clear_and_abort( 404 );
        $m->comp( "list", review_doc => $review );
      } else {
        $m->clear_and_abort( 404 );
      }

    </%perl>


We're using the as of yet unwritten `edit` and `list` components here
to handle requests to /review/new, /review/read, etc.

Notice that we're also using the `get_review_by_id` method in our
Jonas.pm library.  Let's add that into
`/home/username/prj/Jonas/lib/Jonas.pm`.  It looks like so:

<pre><code>
sub get_review_by_id {
  my ( $class, $id ) = @_;
  return  unless  $id;
  my $key = XML::Comma::Storage::Util->concat_key
              ( type => "Review", store => "main", id => $id );
  my $review = eval { XML::Comma::Doc->read( $key ) };
  return $review;
}
</pre></code>


And now `/ob/webtree/jonas/edit`:


    <%args>
      $title     => undef
      $ISBN      => undef
      $publisher => undef
      $author    => undef
      $rating    => undef
      $review    => undef
      %errors    => undef

      $review_doc => undef # for edits
    </%args>
    %
    %
    <%init>
      my $user = $g->auth->force_authn;

      my $post_doc;
      if ( $review_doc ) {
        $post_doc   = XML::Comma::Doc->read( $review_doc->post_key );
        $post_doc->check_authz( $user, 'write' )  or  $m->clear_and_abort( 403 );
      }

    </%init>

    <h3>New Book Review</h3>
    <p>All fields are required.</p>
    %if ( %errors ) {
      <div class="new-review-form-errors">
      <% join "<br />", values %errors %>
      </div>
    % }
    <form method="post" action="<% $g->interface_path( api => "Jonas" ) %>process">
    % if ( $review_doc ) {
      <input type="hidden" name="review_key" value="<% $review_doc->doc_key %>" />
    % }
      <table class="new-review-form">
        <tr>
          <td>Title:</td>
          <td>
            <input type="text" name="title" value="<% $title %>" />
          </td>
        </tr>
        <tr>
          <td>ISBN: </td>
          <td>
            <input type="text" name="ISBN" value="<% $ISBN %>" />
          </td>
        </tr>
        <tr>
          <td>Publisher: </td>
          <td>
            <input type="text" name="publisher" value="<% $publisher %>" />
          </td>
        </tr>
        <tr>
          <td>Author: </td>
          <td>
            <input type="text" name="author" value="<% $author %>" />
          </td>
        </tr>
        <tr>
          <td>Rating: </td>
          <td>
            <select name="rating">
    % foreach my $n ( 1 .. 5 ) {
    %   my $selected = ($rating and $rating == $n)  ?  'selected="1"'  : undef;
            <option value="<% $n %>" <% $selected %>><% $n %></option>
    % }
            </select>
          </td>
        </tr>
        <tr>
          <td>Review: </td>
          <td><textarea rows="15" cols="40"
                        name="review"><% $review %></textarea>
        </tr>
        <tr>
          <td>Submit: </td>
          <td><input type="submit" name="submit" value="submit" /></td>
        </tr>
      </table>
    </form>


This is just a form that posts to `process`.  The most interesting
line is probably `$g->auth->force_authn`, which uses our global `$g`
object, which is our entry point to the Gadgets API.  The `auth`
object is documented at `perldoc Gadgets::AuthManager`, and tells us
that `force_authn` forces authentication in order to access the
resource it is called in.  In other words, folks have to be logged in
in order to write a review.

Another interesting couple of lines are:

<pre><code>
  if ( $review_doc ) {
    $post_doc   = XML::Comma::Doc->read( $review_doc->post_key );
    $post_doc->check_authz( $user, 'write' )  or  $m->clear_and_abort( 403 );
  }
</pre></code>

In a minute we'll see that if the POST to `process` is successful, it
creates a `Gadgets::Standard::Post` doc that contains the review
itself, along with a `Review` doc that contains a pointer to the
`Post`, as well as the meta-data we need to collect to make a `Post` a
`Review`.

If someone is trying to edit a review, we not only make sure that they
are logged in, but that they are authorized to write to that
post/review doc pair.  By default the check_authz will return true for
the owner of the doc, which is what we want.

And now for `/ob/webtree/jonas/process`:


    <%args>
      $title     => undef
      $ISBN      => undef
      $publisher => undef
      $author    => undef
      $rating    => undef
      $review    => undef

      $review_key => undef
      %errors     => ()
    </%args>

    <%init>
      my $user = $g->auth->force_authn;

      my ( $review_doc, $post_doc );
      if ( $review_key ) {
        $review_doc = eval { XML::Comma::Doc->read( $review_key ) };
        $post_doc   = XML::Comma::Doc->read( $review_doc->post_key );
        $m->clear_and_abort( 404 )  if  $@;
        $post_doc->check_authz( $user, 'write' )  or  $m->clear_and_abort( 403 );
      }

      $title      or  $errors{ "title"     } = "Title is required.";
      $ISBN       or  $errors{ "ISBN"      } = "ISNB is required.";
      $publisher  or  $errors{ "publisher" } = "publisher is required.";
      $author     or  $errors{ "author"    } = "author is required.";
      $review     or  $errors{ "review" } = "review is required.";
      $rating =~ /^[1-5]$/  or  $errors{ "rating" } = "Rating must be 1 - 5.";

      if ( values %errors ) {
        $ARGS{ "review_doc" } = $review_doc  if  $review_doc;
        $m->comp( 'edit', %ARGS, errors => \%errors );
        return;
      }


      $review_doc ||= XML::Comma::Doc->new( type => "Review" );
      $post_doc   ||= XML::Comma::Doc->new( type => "Post"   );

      eval {
        # first make our post
        $post_doc->owner( $user->doc_key );
        $post_doc->app( "Jonas" );
        $post_doc->body->html( $review );
        $post_doc->store( store => "main" );

        # and then our review
        $review_doc->post_key( $post_doc->doc_key );
        $review_doc->title( $title );
        $review_doc->ISBN( $ISBN );
        $review_doc->publisher( $publisher );
        $review_doc->author( $author );
        $review_doc->rating( $rating );
        $review_doc->store( store => "main" );
      };
      if ( $@ ) {
        $errors{ "store_error" } = $@;
        $m->comp( "edit", %ARGS, errors => \%errors );
        return;
      } else {
        $m->redirect( "read/" . $review_doc->doc_id );
      }
    </%init>


And for completenes, the `list` component:


    <%args>
      $review_doc => undef
      $order_by   => "record_last_modified DESC"
    </%args>

    <%perl>

      if ( $review_doc ) {
        $m->comp( "listing", key => $review_doc->doc_key );
      } else {
        my $iterator = XML::Comma::Def->Review
                                      ->get_index( "main" )
                                      ->iterator( order_by => $order_by );
        while ( ++$iterator ) {
          $m->comp( "listing", key => $iterator->doc_key );
        }
      }

    </%perl>


    <%def listing>
    <%args>
      $key
    </%args>
    <%init>
      my $review = XML::Comma::Doc->read( $key );
      my $post   = XML::Comma::Doc->read( $review->post_key );
    </%init>

    <% $post->body->html %>
    <hr />
    </%def>


And that's it, we now have a working Jonas application, that can be packaged up into a PAR file and deployed live!

To package it up: ` perl misc/admin/package_devel_gadget.pl --name Jonas --comp_root /ob/webtree/`.

(and the output of `package_devel_gadget.pl --help`:

<pre><code>
  misc/admin/package_devel_gadget.pl --name "Gadget-Name"
     --comp_path "/path/to/mason/components/"

  OPTIONS:

    --name GadgetName

      The name of the gadget you are packaging.  This is required, and
      must be a devel gadget (probably created with
      create_devel_gadget.pl --name).

    --comp_root /mason/comp_root/

      This is the HTML::Mason component_root specified in your
      handler.pl (or otherwise specified in your Apache
      configuration).  This is necissary for the script to find your
      Mason components (and is a required argument), which were
      specified relative to this path with the --comp_path option to
      create_devel_gadget.pl.

    --outfile /path/to/GadgetName.par

      Optionally tell package_devel_gadget.pl where to write out your
      new par file.  This defaults to a par file of the argument you
      provided --name (GadgetName.par) in your present working
      directory.
</code></pre>


And install the production gadget: `perl misc/admin/install_gadget.pl  --par Jonas.par`.

(and the output of `install_gadget.pl --help`:)

<pre><code>
  misc/admin/install_gadget.pl --par "Gadget-Name.par"
     [--uri_path "/uri/to/dispatch/" ]
     [ --par_dir "/prod/pars/dir/" ]

  OPTIONS:

    --par

      The par that we're installing, probably generated by
      package_devel_gadget.pl.  This is required.

    --uri_path

      The uri that will dispatch requests to this gadget.  If
      unspecified the script will try to find a devel gadget of the
      same name and use its path.  It is requred when installing a
      production gadget on a system where the devel gadget doesn't
      exist.

    --pars_path

      The path to the directory where your production pars are
      installed.  This is typically somewhere down apache's
      "document_root", and needs to be readable by an apache child.
      The script will try to figure out your pars_path by looking at
      other non-devel installed gadgets, but will die if it can't
      figure out the path and its unspecified.  So it's kind of
      optional, at least after its been specified once.
</code></pre>

The source to Jonas lives in [https://chronicle.allafrica.com:8080/repository/branches/Gadgets-Oblong/misc/demo/](https://chronicle.allafrica.com:8080/repository/branches/Gadgets-Oblong/misc/demo)
]]></description>
<pubDate>21 Oct 2008 10:10:07 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/07zPdzUR8L3BfCYW</guid>
</item>
<item>
<title>Gadgets Installation</title>
<link>http://gadgets.xml-comma.org/post/main/07zPQvpDWi16TSMl</link>
<author>dug@allafrica.com (Douglas Hunter)</author>
<description><![CDATA[
`mkdir -p /ob/gadgets/build/`

`cd /ob/gadgets/build/`

`perl -MCPAN -e shell; 
  install LWP::UserAgent
  quit`


# Apache/mod-perl

`wget http://www.modssl.org/source/mod_ssl-2.8.31-1.3.41.tar.gz`
`wget http://perl.apache.org/dist/mod_perl-1.0-current.tar.gz`
`wget http://www.poolsaboveground.com/apache/httpd/apache_1.3.41.tar.gz`

UNDER MAC OS X also execute because linking against the system libraries is broken:

`wget http://www.openssl.org/source/openssl-0.9.8g.tar.gz`
`tar xvfz openssl-0.9.8g.tar.gz`
`cd openssl-0.9.8g`
`./config`
`make`

END MAC OS X HUNK

`tar -zxvf apache_1.3.41.tar.gz`
`tar -zxvf mod_perl-1.0-current.tar.gz`
`tar -zxvf mod_ssl-2.8.31-1.3.41.tar.gz`

`cd /ob/gadgets/build/mod_ssl-2.8.31-1.3.41`
`./configure --with-apache=../apache_1.3.41` # MAC OS X: `./configure --with-apache=../apache_1.3.41 --with-ssl=../openssl-0.9.8g`
`cd /ob/gadgets/build/mod_perl-1.30/`

`perl Makefile.PL USE_APACI=1 EVERYTHING=1 SSL_BASE=SYSTEM \ 
APACHE_PREFIX=/ob/gadgets/apache-gadgets/ \ 
APACI_ARGS=--enable-module=ssl,--enable-module=rewrite`

MAC OS X HUNK

`perl Makefile.PL USE_APACI=1 EVERYTHING=1 SSL_BASE=/path/to/downloaded/and/configured/openssl \
APACHE_PREFIX=/ob/gadgets/apache-gadgets/ \
APACI_ARGS=--enable-module=ssl,--enable-module=rewrite`

END MAC OS X HUNK

`make test`
`make install`

inside the Apache::Test install above, path to httpd => /ob/gadgets/apache-gadgets/bin/httpd


# Comma

`mkdir /ob/gadgets/comma`

`/usr/sbin/usermod -a -G apache USERNAME
re-login`

`perl -MCPAN -e shell;
  install Class::ClassDecorator Clone Crypt::Blowfish Crypt::CBC DBI Digest::HMAC_MD5 Inline Lingua::Stem Math::BaseCalc PAR Proc::Exists String::CRC
  get DBD::mysql
  quit`

You may need to do a: sudo yum install mysql-devel.x86_64 if you don't already have the mysql dev files

`cd /root/.cpan/build/DBD-mysql-4.006` # or wherever your CPAN is configured 
                                     # to store build files
`perl Makefile.PL
make test;
make install;`

`cd ..` (back to root of build directory)

`svn co https://chronicle.allafrica.com:8080/repository/trunk/XML-Comma /ob/gadgets/build/XML-Comma`

`cd /ob/gadgets/build/XML-Comma`
`perl Makefile.PL`

`make`

`make test # t/indexing....................ok 1/0grep: 
          # .test/usr/local/comma/log.comma: No such file or directory
          # may fail, that's okay`
`make install`

`yum install gd-devel.x86_64`

`perl -MCPAN -e shell;
force install HTML::FromText
install Apache::DBI Class::Container Convert::UU GD GD::Image::Thumbnail HTML::Mason::Plugin HTML::TreeBuilder i18n JSON Lingua::EN::Numbers MIME::Lite Crypt::Twofish Email::Address`
quit`


# Gadgets

`svn co https://chronicle.allafrica.com:8080/repository/branches/Gadgets-Oblong/ /ob/gadgets/build/Gadgets`

`cd /ob/gadgets/build/Gadgets`
`perl Makefile.pl`
`make`
`make install`

# Qmail

`mkdir /ob/gadgets/mail/`
`mkdir /ob/gadgets/mail/qmail`
`mkdir /ob/gadgets/mail/ucspi-tcp`

`mkdir /ob/gadgets/mail/daemontools`

`mkdir /ob/gadgets/build/qmail`
`cd /ob/gadgets/build/qmail`
`wget http://www.qmail.org/netqmail-1.06.tar.gz`
`wget http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz`
`wget http://cr.yp.to/daemontools/daemontools-0.76.tar.gz`

`tar -zxvf netqmail-1.06.tar.gz`
`tar -zxvf ucspi-tcp-0.88.tar.gz`

`cd /ob/gadgets/build/qmail/netqmail-1.06`
`head -15 INSTALL.ids | grep "#" | perl -lne 's|^\s+#\s+(.*)$|/usr/sbin/$1|; s|/var/|/ob/gadgets/mail/|; print;' > IDS`
`chmod +x IDS`
`./IDS`

`perl -i -p -e 's|/var/qmail|/ob/gadgets/mail/qmail|' conf-qmail`

`make setup check`

`./config-fast gadgets.yourdomain.com`

`cd /ob/gadgets/build/qmail/ucspi-tcp-0.88`
`patch < /ob/gadgets/build/qmail/netqmail-1.06/other-patches/ucspi-tcp-0.88.errno.patch`
`perl -i -p -e 's|/usr/local|/ob/gadgets/mail/ucspi-tcp|' conf-home`

`make`
`make setup check`

NOTE: Now we're going to install the damontools package.  It's not 
easily relocatable, as it needs to write to the inittab during the 
installation, so we'll simply follow the instructions in section 2.7
here http://lifewithqmail.org/lwq.html#installation instead of 
mucking about

`mkdir /package`
`cd /package`
`/ob/gadgets/build/qmail/daemontools-0.76.tar.gz .`
`tar -zxvf daemontools-0.76.tar.gz`
`cd /package/admin/daemontools-0.76/src/`
`patch < /ob/gadgets/build/qmail/netqmail-1.06/other-patches/daemontools-0.76.errno.patch`
`cd /package/admin/daemontools-0.76/`
`package/install`

end damontools installation

<pre><code>
echo "./Maildir/" > /ob/gadgets/mail/qmail/control/defaultdelivery
cp /ob/gadgets/build/qmail/setupfiles/rc /ob/gadgets/mail/qmail/

cp /ob/gadgets/build/qmail/setupfiles/qmailctl /ob/gadgets/mail/qmail/bin/
mkdir -p /ob/gadgets/mail/qmail/supervise/qmail-send/log
mkdir -p /ob/gadgets/mail/qmail/supervise/qmail-smtpd/log

cp /ob/gadgets/build/qmail/setupfiles/qmail-send-run /ob/gadgets/mail/qmail/supervise/qmail-send/run
cp /ob/gadgets/build/qmail/setupfiles/qmail-send-log-run /ob/gadgets/mail/qmail/supervise/qmail-send/log/run

cp /ob/gadgets/build/qmail/setupfiles/qmail-smtpd-run /ob/gadgets/mail/qmail/supervise/qmail-smtpd/run
cp /ob/gadgets/build/qmail/setupfiles/qmail-smtpd-log-run /ob/gadgets/mail/qmail/supervise/qmail-smtpd/log/run

chmod 755 /ob/gadgets/mail/qmail/supervise/qmail-send/run
chmod 755 /ob/gadgets/mail/qmail/supervise/qmail-send/log/run
chmod 755 /ob/gadgets/mail/qmail/supervise/qmail-smtpd/run
chmod 755 /ob/gadgets/mail/qmail/supervise/qmail-smtpd/log/run

mkdir -p /ob/gadgets/mail/log/qmail/smtpd
chown qmaill /ob/gadgets/mail/log/qmail /ob/gadgets/mail/log/qmail/smtpd

echo 20 > /ob/gadgets/mail/qmail/control/concurrencyincoming
echo '127.:allow,RELAYCLIENT=""' >>/ob/gadgets/mail/ucspi-tcp/tcp.smtp
/ob/gadgets/mail/qmail/bin/qmailctl cdb

/sbin/chkconfig sendmail off

cp /ob/gadgets/build/Gadgets/misc/bin/email_wiki_posts_from_cron.pl /ob/gadgets/mail/qmail/bin/
cp /ob/gadgets/build/Gadgets/misc/bin/process_email_relay.pl /ob/gadgets/mail/qmail/bin/
cp /ob/gadgets/build/Gadgets/misc/bin/process_email_wikipost.pl /ob/gadgets/mail/qmail/bin/
cp /ob/gadgets/build/Gadgets/misc/bin/process_email_wikireply.pl /ob/gadgets/mail/qmail/bin/

chown root:qmail /ob/gadgets/mail/qmail/bin/*.pl

cp /ob/gadgets/build/qmail/setupfiles/qmail* /ob/gadgets/mail/qmail/alias/

chown -R apache:nofiles /ob/gadgets/comma/
chmod -R 664 /ob/gadgets/comma/

ln -s /ob/gadgets/mail/qmail/supervise/qmail-send /service
ln -s /ob/gadgets/mail/qmail/supervise/qmail-smtpd /service
</code></pre>

]]></description>
<pubDate>21 Oct 2008 09:55:07 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/07zPQvpDWi16TSMl</guid>
</item>
<item>
<title>Gadgets Documentation Index</title>
<link>http://gadgets.xml-comma.org/post/main/07zPOsC-Hian0HW3</link>
<author>dug@allafrica.com (Douglas Hunter)</author>
<description><![CDATA[[[titled_link( "Gadgets Installation" )]]

[[titled_link( "New Gadget Tutorial" )]]

[[titled_link( "Gadgets API" )]]

[[titled_link( "Putting it all Together" )]]

[[titled_link( "Gadgets HTTP POST API" )]]]]></description>
<pubDate>21 Oct 2008 09:52:56 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/07zPOsC-Hian0HW3</guid>
</item>
<item>
<title>re: Gadgets goes Public</title>
<link>http://gadgets.xml-comma.org/post/main/04ED8BJ9i14MlSdV</link>
<author>amanda@velociraptor.info (Amanda Bee)</author>
<description><![CDATA[<p>I'm looking for a few specific pieces of information about Gadgets and not sure where to find them. Very recently, something happened so that this site has begun appearing in French, which isn't the end of the world (I've been meaning to brush up on my French) but also isn't the easiest thing to navigate.</p><p>Is there a gadgets users email list?</p><p>Most of my current questions belong on such a list--that is all I am looking for here. Just for kicks, the questions I'd put to a list if I find one include:</p><p>* How can users change their own passwords?</p><p>* Does the wiki include any kind of &quot;sections&quot; functionality? In some wiki's you can edit sections of a page independently, rather than editing the whole page. </p>]]></description>
<pubDate>23 Oct 2006 15:39:56 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/04ED8BJ9i14MlSdV</guid>
</item>
<item>
<title>asdfasdf</title>
<link>http://gadgets.xml-comma.org/post/main/049VhKEzCAycb_yh</link>
<author>ski@allafrica.com (ski)</author>
<description><![CDATA[<p>this should be a conflict<br /></p>]]></description>
<pubDate>09 Oct 2006 08:40:20 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/049VhKEzCAycb_yh</guid>
</item>
<item>
<title>Feeling testy</title>
<link>http://gadgets.xml-comma.org/post/main/041UE4DV9nthbVog</link>
<author>inti.einhorn@gmail.com (ieinhorn)</author>
<description><![CDATA[<p>testy testy </p>]]></description>
<pubDate>15 Sep 2006 00:28:53 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/041UE4DV9nthbVog</guid>
</item>
<item>
<title>hello</title>
<link>http://gadgets.xml-comma.org/post/main/040yiRR4BYCClYuX</link>
<author>chongs@newschool.edu (Siu Chong)</author>
<description><![CDATA[<p>just test driving </p>]]></description>
<pubDate>13 Sep 2006 11:29:32 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/040yiRR4BYCClYuX</guid>
</item>
<item>
<title>post locking</title>
<link>http://gadgets.xml-comma.org/post/main/03zb12yDP83UV-Ps</link>
<author>ski@allafrica.com (ski)</author>
<description><![CDATA[<p>When you click the <i>details &amp; actions </i>popup and select&quot;edit&quot;, Gadgets &quot;checks out&quot; this document to indicate that you are working on it. When you save your changes, Gadgets will &quot;check in&quot; the document. If someone tries to edit the same page while you have it &quot;checked out&quot;, Gadgets will give them a warning message that the you have the post checked out, and when you checked it out. <br /><br />Note that this checkout/checkin system is completely advisory - that is, anyone who has permissions to edit this document can overwrite your lock by simply clicking the &quot;override lock&quot; link. This is necessary because someone may click a back or forward button on their browser, lose a connection to the internet, etc. resulting in them accidentally/permanently locking a page.</p><p><i>Note</i>: If you need to truly lock a page so that only you can edit it, you can change the post's edit permissions such that you are the only person allowed to edit it.<br /></p>]]></description>
<pubDate>06 Sep 2006 06:53:55 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03zb12yDP83UV-Ps</guid>
</item>
<item>
<title>finding posts</title>
<link>http://gadgets.xml-comma.org/post/main/03zavr6CHyljU0cJ</link>
<author>ski@allafrica.com (ski)</author>
<description><![CDATA[<p>There are many ways to find a post in Gadgets.</p><p>The most intuitive is to just enter the text you are looking for in the search box in the upper right hand corner of the site. However, not all sites have this, so if your site does not, you can find a link to the search page in the navigation bar at the top, to the left of your name. The search page will search all documents which you have permissions to read, even if they are hidden from the general public. This can be confusing if you are not logged in for some reason.</p><p>Another way to search for a post is to just replace the last part of your URL, and type in what you are looking for. For example, if you are on a page like this:</p><blockquote><p><a href="/post/main/03zaGGTj-vSBnABc">http://gadgets.xml-comma.org/post/main/03zaGGTj-vSBnABc</a></p></blockquote><p>or this:</p><blockquote><p><a href="/post/main/Gadgets%20TODO">http://gadgets.xml-comma.org/post/main/Gadgets TODO</a></p></blockquote><p>you can simply highlight all text after /, and type in what you are looking for, like so:</p><blockquote><a href="/post/main/User%20Documentation">http://gadgets.xml-comma.org/post/main/User Documentation</a><br /></blockquote><p>This will take you directly to the oldest page with such a title - if none or more than one exists, it will take you to the search page.<br /><br />Alternately, there are the &quot;recent activity&quot; and &quot;tags&quot; pages, also accessible from the navigation bar to the left of your user name, which let you sort posts by when they were last edited or created, and what tags they have, respectively.</p><p>Any drafts you have saved but not posted will not appear in any of these interfaces, however, so to find them, you can move your mouse over the <i>you are: yourname </i>navigation widget, and a box will pop up with a link to &quot;draft posts&quot;.</p><p></p><p><br /></p>]]></description>
<pubDate>06 Sep 2006 06:47:19 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03zavr6CHyljU0cJ</guid>
</item>
<item>
<title>Config Values</title>
<link>http://gadgets.xml-comma.org/post/main/03zaGGTj-vSBnABc</link>
<author>ski@allafrica.com (ski)</author>
<description><![CDATA[<p>All config values can be set by the script misc/admin/add_config.pl from the Gadgets source. We hope to soon have a web based interface that allows these commands, but it isn't there yet. Note that although the script is named add_config.pl, it will only add a configuration if one isn't already there. So, for example, to change the width of the editor that comes up when you create or edit a post, you can simply run:</p><blockquote><p><i>misc/admin/add_config.pl --gadget ATemplate --path / --key editor_width --value 500px </i><br /></p></blockquote><p>All configuration values have 4 parts, the gadget and path (which describe where the configuration value is used), the key (what is being set), and the value (what the key is set to). Gadget and path must both mach for a configuration value to apply on a given page, so if we had set the path to something that didn't start with <b>/new-post/</b>, it would not take effect. Similarly, the editor_width value isn't checked on any pages that are not part of the ATemplate Gadget (even though their path is under <b>/</b>)<br /></p><p>What follows is a relatively comprehensive list of configuration values that can be set, and a description of what they do:</p><ul><li>Gadget<b> ATemplate</b>'s keys:<br /></li><ul><li>template_dir - this is a unix path, relative to your webtree's root, where the <i>header</i>, <i>footer</i>, etc. files for changing the look of your site can be found.<br /></li><li>editor_width - this is the width, in pixels, of the box used to create new or edit old posts.<br /></li></ul><li>Gadget <b>Wiki</b>'s keys:</li><ul><li>post_details_pos - this is either the string <i>top</i> or <i>bottom</i>, and indicates where to put the details (author, last edited time, etc.) specified in post_details_elements - note that this is NOT used to control where the &quot;details and actions&quot; drop-down box appears - Gadgets tries to determine that automatically.<br /></li><li>post_details_elements - this is a comma seperated string of values <i>creator</i> <i>last_editor</i> <i>parent_info</i> <i>tags</i> <i>attachments_non_image</i> <i>attachments_image.</i> It defaults to showing the creator, information about a parent post if this post is a reply, tags, and a list of any non-image attachments.<br /></li></ul><li>Gadget <b>core</b>'s keys:</li><ul><li>email_domain_name - the domain name of your site<br /></li><li>email_pretty_name - the human-readable name of your site for use in emails, e.g. <i>Our Gadgets Planning Wiki</i><br /></li><li>email_template_dir - a unix path, relative to the root of your filesystem, where the files for changing the look of emails from your site can be found.</li></ul><li>Gadget <b>core_auth</b>'s keys:</li><ul><li>read</li><li>admin</li><li>write</li><li>post_reply</li><li>post_new</li></ul></ul><p>Note that there are two special values for configurations under core_auth, <b>*</b><b></b> and <b>all</b> (without quotes). <b>all </b>indicates that any logged in user has this priviledge. <b>* </b>indicates that anyone, logged in or not, has this priviledge. Otherwise, we take a list of GadgetsGroup and GadgetsUser doc_key's. For example, to set up the admin interface on this site so that only folks in the Administrator group can use it, we run:</p><blockquote><p>/usr/local/src/gadgets/misc/admin/add_config.pl --gadget core_auth --path /admin/ --key read --value GadgetsGroup|main|02K_2U7EUvEL</p><p>/usr/local/src/gadgets/misc/admin/add_config.pl --gadget core_auth --path / --key admin --value GadgetsGroup|main|02K_2U7EUvELH01e <br /></p></blockquote><p>This tells Gadgets that only users in the group administrators GadgetsGroup|main|02K_2U7EUvEL (which happens to be named Administrators here) may access the folder <b>/admin/ </b>and everything under it. Note that the second command is slightly different - the only thing this command does is add a link to the administration section under the &quot;<i>you are: yourname</i>&quot; popup box.</p><p>The <i>post_reply</i> and <i>post_new</i> configurations are a little different - these specifty whether a user can post replies or new posts (these constraints are checked <i>in addition</i> to other permissions, which a user may be set in the configuration or by a user when they post an item). For example, to limit your Gadgets installation so that anyone (logged in or not) can reply, but only logged in users can make new posts, you could run this:<br /></p><blockquote><p>/usr/local/src/gadgets/misc/admin/add_config.pl --gadget core_auth --path / --key post_reply --value '*'</p><p>/usr/local/src/gadgets/misc/admin/add_config.pl --gadget core_auth --path / --key post_new --value all<br /></p></blockquote><p><br /></p>]]></description>
<pubDate>06 Sep 2006 06:01:53 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03zaGGTj-vSBnABc</guid>
</item>
<item>
<title>Gadgets Documentation</title>
<link>http://gadgets.xml-comma.org/post/main/03za0Ic4pE2jnBdw</link>
<author>ski@allafrica.com (ski)</author>
<description><![CDATA[<p>This page is a placeholder for all documentation about how to use, administer, and further develop Gadgets.<br /></p><ul><li><a href="/util/title-link/User%20Documentation" name="gadget-cleanup-wikilink" class="" title="gadget-cleanup-wikilink">User Documentation</a><ul><ul><li>How to register and log in<br /></li></ul><ul><li>How to configure your email settings</li></ul><ul><li>How to post</li></ul><li><a href="/util/title-link/finding%20posts" name="gadget-cleanup-wikilink" title="gadget-cleanup-wikilink" class="">finding posts</a></li><li><a href="/util/title-link/post%20locking" name="gadget-cleanup-wikilink" title="gadget-cleanup-wikilink" class="">post locking</a></li></ul></li><li>Administrator Documentation</li><ul><li><a href="http://gadgets.xml-comma.org/util/title-link/Config%20Values" name="gadget-cleanup-wikilink" title="gadget-cleanup-wikilink" class="">Config Values</a></li></ul><ul><li>Using the <a href="http://gadgets.xml-comma.org/util/title-link/admin%20interface" name="gadget-cleanup-wikilink" title="gadget-cleanup-wikilink" class="">admin interface</a></li></ul><li><a href="/util/title-link/Developer%20Documentation" name="gadget-cleanup-wikilink" class="" title="gadget-cleanup-wikilink">Developer Documentation</a></li><ul><li>writing new gadgets</li><li><a href="/util/title-link/Gadgets%20TODO" name="gadget-cleanup-wikilink" class="" title="gadget-cleanup-wikilink">Gadgets TODO</a></li></ul><li><a href="https://chronicle.allafrica.com:8080/repository/trunk/Gadgets/">Subversion repository</a> (for download)<a href="/util/title-link/Gadgets%20TODO" name="gadget-cleanup-wikilink" class="" title="gadget-cleanup-wikilink"></a></li></ul><p><br /></p><p><br /></p>]]></description>
<pubDate>06 Sep 2006 05:44:51 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03za0Ic4pE2jnBdw</guid>
</item>
<item>
<title>re: Gadgets goes Public</title>
<link>http://gadgets.xml-comma.org/post/main/03vcPqPD1OXaGx7w</link>
<author>allan.stanley@gmail.com (killapop)</author>
<description><![CDATA[<p>Hey doug!</p><p>hows it hamming!</p><p>how do i download and install gadgets on my server?</p><p>pls advise.</p><p>Rev. Rupee - a.k.a killapop</p>]]></description>
<pubDate>25 Aug 2006 05:12:22 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03vcPqPD1OXaGx7w</guid>
</item>
<item>
<title>testing filename translation</title>
<link>http://gadgets.xml-comma.org/post/main/03rdTYHYcMBfPQSZ</link>
<author>ski@allafrica.com (ski)</author>
<description><![CDATA[<p>bla </p>]]></description>
<pubDate>13 Aug 2006 03:08:19 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03rdTYHYcMBfPQSZ</guid>
<enclosure url="http://gadgets.xml-comma.org/file/main/03rdTYHYcMBfPQSZ/VRNIqZ6A" length="304" type="text/rtf" />

</item>
<item>
<title>thumbnail after</title>
<link>http://gadgets.xml-comma.org/post/main/03pwjWmZG-fq93hs</link>
<author>ski@allafrica.com (ski)</author>
<description><![CDATA[<p>and the significant improvement here... </p>]]></description>
<pubDate>07 Aug 2006 23:25:21 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03pwjWmZG-fq93hs</guid>
<enclosure url="http://gadgets.xml-comma.org/file/main/03pwjWmZG-fq93hs/yoH40hYx" length="12952" type="image/jpeg" />

<enclosure url="http://gadgets.xml-comma.org/file/main/03pwjWmZG-fq93hs/CeOrrcQt" length="10230" type="image/png" />

</item>
<item>
<title>thumbnail before</title>
<link>http://gadgets.xml-comma.org/post/main/03pwirBV6Sx_xqSA</link>
<author>ski@allafrica.com (ski)</author>
<description><![CDATA[<p>note the pixelation and crudiness of the thumbnail... </p>]]></description>
<pubDate>07 Aug 2006 23:24:39 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03pwirBV6Sx_xqSA</guid>
<enclosure url="http://gadgets.xml-comma.org/file/main/03pwirBV6Sx_xqSA/UoM3BuqF" length="12952" type="image/jpeg" />

<enclosure url="http://gadgets.xml-comma.org/file/main/03pwirBV6Sx_xqSA/NXNndIen" length="10230" type="image/png" />

</item>
<item>
<title>re: Testing a wiki</title>
<link>http://gadgets.xml-comma.org/post/main/03p0_MCSO_PIZf7o</link>
<author>yellowsky4ever@yahoo.com (tsukaremasu)</author>
<description><![CDATA[<p>Oh... how does this gadget work? Hmm...</p>]]></description>
<pubDate>05 Aug 2006 04:06:47 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03p0_MCSO_PIZf7o</guid>
</item>
<item>
<title>Testing a wiki</title>
<link>http://gadgets.xml-comma.org/post/main/03p0ZKx7a9BFbfjD</link>
<author>yellowsky4ever@yahoo.com (tsukaremasu)</author>
<description><![CDATA[<p>Interesting.</p><p>A <b>wiki</b> (<a href="http://gadgets.xml-comma.org/wiki/International_Phonetic_Alphabet_for_English" title="International Phonetic Alphabet for English">IPA</a>: [&Euml;&#136;w&Eacute;&ordf;.ki&Euml;&#144;] &lt;WICK-ee&gt; or [&Euml;&#136;wi&Euml;&#144;.ki&Euml;&#144;] &lt;WEE-kee&gt;<a href="http://gadgets.xml-comma.org/new-post/#_note-0" title="">[1]</a>) is a type of <a href="http://gadgets.xml-comma.org/wiki/Website" title="Website">website</a> that allows users to easily add, remove, or otherwise <a href="http://gadgets.xml-comma.org/wiki/Edit" title="Edit">edit</a> and change some available content, sometimes without the need for registration. This ease of interaction and operation makes a wiki an effective tool for <a href="http://gadgets.xml-comma.org/wiki/Collaborative_writing" title="Collaborative writing">collaborative authoring</a>. </p>]]></description>
<pubDate>05 Aug 2006 04:05:41 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03p0ZKx7a9BFbfjD</guid>
</item>
<item>
<title>Gadgets TODO</title>
<link>http://gadgets.xml-comma.org/post/main/03eJwJWUgTm0Rdlc</link>
<author>dug@allafrica.com (Douglas Hunter)</author>
<description><![CDATA[<p>NOTE:</p><ul><li>grumble grumble install process grumble grumble.</li><ul><li>installation process:</li><ul><li>1 - add a user, and bless them into the administrators group</li></ul><ul><li>2 - run these commands:</li><ul><li>sudo /usr/local/src/gadgets/misc/admin/add_config.pl --gadget core_auth --path /me/ --key read --value all</li><li>sudo /usr/local/src/gadgets/misc/admin/add_config.pl --gadget core_auth --path /admin/ --key read --value #get admin group doc_key here</li><li>sudo /usr/local/src/gadgets/misc/admin/add_config.pl --gadget core_auth --path / --key admin --value #get admin group doc_key here <br /></li></ul></ul></ul></ul><p>BUGS:</p><ul><li>handler IP/netmask addresses cleaner, allow support for skipping login check<br /></li><li>allow admin to set a site-wide default permission config<br /></li><li>do translation the Right Way<br /></li><li>don't give a 500 internal saerver error when you can't find a par file, give something more friendly - nuke ARegistration.par and click on register for example</li><li>Get rid of this stupidity: &quot;Could not erase this post, error was: actually erasing ok.&quot;<br /></li><li>don't give a 403 error for mysql server going away (wtf?)<br /></li><li>further work on the docs</li><li>configure order of posts on a per-list page basis (home page and blog are the two current modes - should we introduce the ability o extend this arbitrarily many?).<br /></li><li>/util/erased should redirect to parent if we just deleted a reply<br /></li><li>health and travel wikis are occasionally fubar'ing with user not exist errors again... wtf?!? a couple of things going on here:<br /></li><ul><li>authmanager seems to use host, not host+port specific cookies, so when tunneling in that will give problems since everything is on localhost<br /></li></ul><ul><li>the famous sql server goes away and dies bug..</li></ul><li>it'd be nice to support &quot;friendlier&quot; titles like on wikipedia - underscore == space == %20 ... what do we need to change to do this?<br /></li><li>Attachment names and titles don't work with search, according to Phillip.</li><li>new_post/new_reply checks:</li><ul><li>details and actions should reflect the new permission stuff</li><li>do defaults for new_post/new_reply &quot;The Right Way&quot;</li><li>do something magic for the header to hide new post when it is not an option</li></ul><li>anonymous users:</li><ul><li>to do anon users justice, we'll need to rejigger details and actions to not say &quot;please log in&quot;... (could be a config option if it asks you to sign in or not?) - how about checking if there are any perms available to anoncowards, and if so, displaying them, if not displaying a more useful message than please log in (detail why we are asking to log in).<br /></li><li>Introduce a &quot;All users (including anon)&quot; option in permissions lists. which maps to * on the backend<br /></li></ul><li>wiki.tt.o folks would also like recent posts tab to be hidden from non-administrators. to do this we need a more robust templating framework for the header, etc.<br /></li><li>we can stilll get an infinite redirect under /pub/stat (others?) w/ \&quot; @ end of URL... then we get ...index.html/index.html/...<br /></li><li>email setups broken ???</li><li>NOTE: there is/was something wrong with gadgets configuration that will defeat even an index rebuild - you need to re-add all the config values, presumably so the cache is re-set.<br /></li><li>preview body should add a horizontal break and an anchor and go to that anchor...<br /></li><li>gadgets calendar is a priority</li><li>visual diff (use alt? how does wikipedia do it? also look @ trac) - use these on a) revision pages b) force_save page (the page you get when you post and someone has stolen your lock from underneath you) - also consider a JS diff - see http://en.wikipedia.org/wiki/User:Cacycle/diff<br /></li><li>wiki-source/mason/layout/post could use a rewrite/overhaul </li><li>locking stuff: catch other places a write might happen... find these with a pre_store_hook, probably.<br /></li><li>force save screen doesn't inherit layout - why? when we get diffs, this is the place to show them<br /></li><li>we should get autopopulate options for the tag field. no reason not to. particularly blog and home page but also all other tags tht exist. people DON't HAVE to click on them, and acn type their own...<br /></li><li>there seems to be some sort of off by one error in terms of who did what revision...</li><li>we die with an internal server error when we can't find the par file refeerenced in the gadget. catch this error and do somethig slightly less scary.<br /></li><li>conflicts stuff should catch the case when someone overrode your lock, and give you the option to 1) merge 2) overwrite 3) abandon<br /></li><li>add writable interface to ConfigManager, admin auths...<br /></li><li>you need to be signed on to get a list of users. not a big deal, but should be fixed eventually.<br /></li><li>deleting users seems to do funky (read: really bad) things. for now we should just set a &quot;disabled&quot; flag. That way old reference to that login in edit histories, etc. won't go ape.<br /></li><li>shouldn't ARegistration ask for your password twice and star it out as you type?<br /></li><li>search wierdness with multiple search terms?<br /></li><li>you can't search for a user by email address (only by nickname and full name) - this is actually a privacy/security FEATURE. but we should eventually give users a checkbox or so to enable this.</li><li>there is something weird with our mason stuff when you try to do a &lt;%method&gt;<br /></li><li>multi store indexing brokenness (a comma-devel bug)<br /></li><li>'GadgetsConfiguration' neq 'GadgetConfiguration', storename and dirname mismatch</li><li>have a look at Perl::Critic to analyze code for common mistakes that dug may have made ;-)</li><li>tinymce fixes:</li><ul><li>weird stuff with bullets</li><li>colors</li><li>tables/divs</li><li>raw html button<br /></li></ul><li>Audit all pre and post store hooks that do cleanup (when posts move, get deleted, when users or groups are deleted, etc) ie a post gets deleted, all previous versions should be nuked, when a user is deleted, subscription isn't deleted, etc.</li><li><b>resolved</b>: (8/23) backup scripts for chronicle repository and the chronicle xen stuff<br /></li><li><b>resolved</b>: there must be a way to hook the dummy cache_static.macro install into the make install step instead of in perl Makefile.PL</li><li><b>resolved</b>: (rev 640) Images display in image rendering program instead of coming up in firefox - fixed.</li><li><b>resolved</b>: (rev 641) this means that filenames are not preserved, which is especially problematic for non-image files... to fix, change link to file/X/Y to file/X/Y/$name to fix this ?<br /></li><li><b>resolved</b>: (rev 639) installs w/o Cache::Static require a dummy cache_static.macro (contains nothing but 1;) - we should invoke misc/install-extras.pl from Makefile.PL<br /></li><li><b>resolved</b>: (rev 637) not logged on -&gt; click &quot;new post&quot; -&gt; red &quot;you must log in&quot; message. this is check_auth not refreshing - also &quot;perhaps you should log on&quot; loads when it shouldn't if you get redirected to that page. (but a reload removes the message.) - how can we refresh without resorting to a force_auth ( redirect =&gt; 0 ) call?</li><li><b>resolved</b>: (rev 632) add config values for post_new and post_reply (needed for wiki.tt.o, and probably will be useful for others)<br /></li><li><b>resolved</b>: (rev 629) get some Cache::Static goodness up in this piece.<br /></li><li><b>resolved</b>: (rev 554) Authentication required to view photo thumbnails</li><li><b>resolved</b>: (rev 546) when a post is rendered on the front page and has an image, and has details at the top, the image shows to the left of the details and actions&gt;&gt; instead of below it. We need a br / somewhere fixed by adding CSS: .post .image-thumbnails { clear: right; }</li><li><b>resolved</b> (rev 540): locking stuff: revert/unlock &quot;button&quot; @ bottom of editor window in edit</li><li><b>resolved</b>: (rev 539) conflicts stuff is a priority (warning if it appears you have overwritten someone else's changes could work)</li><li><b>resolved</b>: (rev 532, handler.pl fix) improve poor thumbnail quality by telling GD to use true color, not pallette. see http://gadgets.xml-comma.org/post/main/03pwirBV6Sx_xqSA for example.<br /></li><li><b>resolved</b>: (rev 531, APost) gadgets gives you no indication that it can't find a user/group you've typed in by hand w/o choosing something from the autocomplete magique. make some sort of javascripty popup here.</li><li><b>resolved</b>: attachments, tags, author name, etc. should be listed at the bottom by default on wiki.tacticaltech.org, and we should have a GConf for this ( post_details_pos and post_details_elements in Wiki ). Images are now excluded from the attachments display by default. Also, add a last modified by field to this display..</li><li><b>resolved</b>: add support for user ACLs to search, etc. code</li><li><b>resolved</b>: restore sort by changed/created time in wiki-source/mason/layout/posts_table (pass an argument, remove order_by from iterator)<br /></li><li><b>resolved</b>: MRTG for watching memory usage on xen-master, update images to include snmpd with reasonable config.</li><li><b>resolved</b>: apply search permission trick to &quot;posts by&quot; and &quot;recent activity&quot;</li><li><b>resolved</b>: search now pulls any documents you have permission for<br />instead of just public posts<br /></li><li><b>resolved</b>: install osidc as 72.3.176.214</li><li><b>resolved</b>: save draft -&gt; publish broken on wiki.tt.o (probably some other subdomains too - fixed by replacing perl with standard debian perl and recompiling apache, moving/reinstalling/upgrading perl modules, etc.)<br /></li><li><b>mostly resolved</b>: also proof the css/html to make things &quot;most&quot; standard compliant</li><ul><li>as of rev 503, the following URLs pass validation:</li><ul><li>/index.html</li><li>/login.html</li><li>/pub/blog</li><li>/pub/stat</li><li>/pub/tag</li></ul><li>(no logged in pages have been validated yet)<br /></li></ul><li><b>resolved</b>: upgrade debian packages on xen-master and all xen domains for security reasons<br /></li><li><b>resolved</b>: Get xendomain template running latest gadgets modules (as of 07/08/2006). Also add apache init.d/rc*.d scripts to all xen domains for ease of restart.</li><li><b>resolved</b>: fix path handling for rss button returns Wiki par file</li><li><b>resolved</b>: layout of register link - it should be on the right</li><li><b>resolved</b>: dhandler in /wiki should check for article name match if it can't find a doc key match</li><li><b>resolved</b>: hitting a nonexistant url, such as<br /><a target="_blank" href="http://gadgets.xml-comma.org/asdfasdf/">http://gadgets.xml-comma.org/asdfasdf/</a> redirects indefinitely.<br /></li><li><b>resolved</b>: user admin should have links to user history</li><li><b>resolved</b>: $gadget = $self-&gt;conf-&gt;get( gadget =&gt; 'core_impl',<br /> path =&gt; $args{ path },<br /> key =&gt; $args{ api } ); in resolver is returning<br /> a true value when it should return a string.</li><li><b>resolved</b>: gadgets installs for travel + health on chronicle</li></ul><p></p><p>EXTRAS:</p><ul><li><b>resolved</b>: Arbitrary number of columns stored @ la footer/header in wiki-template, do the &quot;Right Thing&quot;</li><li><b>resolved</b>: by default, put quicksearch form in the header.</li><li><b>resolved</b>: so browser w/o js can at least browse gadgets sites, audit script blocks and standardize-ify them<br /></li><li>from Tami/Reed: approve-before-post functionality</li><li>from Tami/Reed: automate permissions and install process</li><li>from Tami/Reed: automagic sitemap as ttech folks were describing<br /></li><li>from Tami/Reed: friendly URLS by default, death to hashes in wiki<br /></li><li>from Tami/Reed: search by tag, etc. - more options</li><li>from Allan: we need a way to control whether main2, etc. go on the right or left hand side, because order can matter.</li><li>from Marek: need a way to find unlinked-to pages and unlinked-to pages from admin interface - what's the best way?<br /></li><li>internationalization support (low priority)<br /></li><li>rssolator-style aggregator gadget<br /></li><li>think about where we need AJAX (and the inherent round trip latency) vs. where we can get along with plain js...<br /></li><li>auto-populate tags w/ home page, blog &amp; all tags that have been used. home page &amp; blog are &quot;special&quot; and at the top. blog should default to having reply all permissions<br /></li><li>put in a &quot;permalink&quot; function that links to the title instead of the ugly hash ?<br /></li><li>commit message with each post would be nice to provide more wiki-esque functionality and to help the visual diffs.<br /></li><li>It'd be nice if the admin interface also had a web-accessible template editor.</li><li>It'd be super cool if we could seperate &quot;widgets/mini-gadgets&quot; from display. IE, you could move the navbar to the footer for example.</li><li>Allan has some good ideas about the RSS/more info links.</li><li>js autocompletion goodness for tags (but be sure to allow arbitrary input as well)</li><li>browser compatibilty (stats from mm.o, oct 3 2005):</li><ul><li>ie6 works (47%). firefox works (15%).</li></ul><ul><li>ie5.5 (3%) login seems broken?</li><li>opera (&lt;1%) seems to mostly work...<br /></li><li>safari (5%) bugs:</li><ul><li>logout is broken</li><li>details and actions dropdown falls away before you can select anything</li></ul></ul><li>18% netscape (compatible) could be ie5.5 ? (eep)</li></ul><p><br /></p><p></p>]]></description>
<pubDate>03 Jul 2006 17:08:36 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03eJwJWUgTm0Rdlc</guid>
</item>
<item>
<title>re: foo</title>
<link>http://gadgets.xml-comma.org/post/main/03dZkyP6OCXQPl9e</link>
<author>ski@allafrica.com (ski)</author>
<description><![CDATA[<p>more?</p><p></p>]]></description>
<pubDate>01 Jul 2006 10:17:34 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03dZkyP6OCXQPl9e</guid>
<enclosure url="http://gadgets.xml-comma.org/file/main/03dZkyP6OCXQPl9e/pcr56q7v" length="69804" type="image/jpeg" />

</item>
<item>
<title>test frontpage</title>
<link>http://gadgets.xml-comma.org/post/main/03ajoQmVs1utAn_F</link>
<author>ski@allafrica.com (ski)</author>
<description><![CDATA[<p>test </p>]]></description>
<pubDate>22 Jun 2006 20:27:07 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03ajoQmVs1utAn_F</guid>
</item>
<item>
<title>test safari post #2</title>
<link>http://gadgets.xml-comma.org/post/main/03YQVyFHnpESiVsJ</link>
<author>ski@allafrica.com (ski)</author>
<description><![CDATA[]]></description>
<pubDate>12 Jun 2006 18:53:50 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03YQVyFHnpESiVsJ</guid>
</item>
<item>
<title>re: Gadgets goes Public</title>
<link>http://gadgets.xml-comma.org/post/main/03SoWzG9W_eiUcDd</link>
<author>amaba@allafrica.com (Amadou Mahtar Ba)</author>
<description><![CDATA[<p>Hi Doug, testing your gadget </p>]]></description>
<pubDate>26 May 2006 18:27:11 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03SoWzG9W_eiUcDd</guid>
</item>
<item>
<title>Going to South Africa</title>
<link>http://gadgets.xml-comma.org/post/main/03SoOmaCmQG6sc2P</link>
<author>amaba@allafrica.com (Amadou Mahtar Ba)</author>
<description><![CDATA[<p>Leving 4 Jnb Sat 27 4 meetings.</p><p>Already have images.</p>]]></description>
<pubDate>26 May 2006 18:18:26 GMT</pubDate>
<guid>http://gadgets.xml-comma.org/post/main/03SoOmaCmQG6sc2P</guid>
<enclosure url="http://gadgets.xml-comma.org/file/main/03SoOmaCmQG6sc2P/GDxXkmIU" length="2364562" type="image/jpeg" />

</item>
</channel>
</rss>
