[Rt-commit] r9463 - in rtfm/branches/2.3-EXPERIMENTAL/html: Callbacks/RTFM/SelfService Callbacks/RTFM/SelfService/Elements Callbacks/RTFM/SelfService/Elements/Tabs SelfService SelfService/RTFM SelfService/RTFM/Article/Elements SelfService/RTFM/Elements

sunnavy at bestpractical.com sunnavy at bestpractical.com
Thu Oct 25 16:08:09 EDT 2007


Author: sunnavy
Date: Thu Oct 25 16:08:08 2007
New Revision: 9463

Added:
   rtfm/branches/2.3-EXPERIMENTAL/html/Callbacks/RTFM/SelfService/
   rtfm/branches/2.3-EXPERIMENTAL/html/Callbacks/RTFM/SelfService/Elements/
   rtfm/branches/2.3-EXPERIMENTAL/html/Callbacks/RTFM/SelfService/Elements/Tabs/
   rtfm/branches/2.3-EXPERIMENTAL/html/Callbacks/RTFM/SelfService/Elements/Tabs/Default
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Display.html   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/LinkEntryInstructions   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/SearchByCustomField   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/SelectSavedSearches   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowLinks   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowSavedSearches   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowSearchCriteria   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowSearchResults   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowTopics   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Search.html   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/Error   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/GotoArticle   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/Header   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/QuickSearch   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/SelectClass   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/ShowTopic   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Topics.html   (contents, props changed)
   rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/autohandler   (contents, props changed)

Log:
initial search support for SelfService, much refactor is needed

Added: rtfm/branches/2.3-EXPERIMENTAL/html/Callbacks/RTFM/SelfService/Elements/Tabs/Default
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/Callbacks/RTFM/SelfService/Elements/Tabs/Default	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,28 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+<%INIT>
+if ($session{'CurrentUser'}->HasRight( Right => 'ShowArticle',
+				       Object => $RT::FM::System )) {
+        $tabs->{D} = { title => loc('Search Articles'),
+                         path => 'SelfService/RTFM/Article/Search.html',
+                       }
+}
+</%INIT>
+<%ARGS>
+$tabs => {}
+</%ARGS>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Display.html
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Display.html	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,62 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+
+<& /SelfService/Elements/Header, Title => loc('Display Articles') &>
+<h2><&|/l&>Basics</&></h2>
+<span class="label"><&|/l&>Class</&></span>: <span class="value"><%$article->ClassObj->Name%></span><br />
+<em><span class="value"><%$article->Summary%></span></em>
+<h2><&|/l&>Content</&></h2>
+<& /Elements/ShowCustomFields, Object => $article &>
+
+<h2><&|/l&>Links</&></h2>
+<& Elements/ShowLinks, article => $article &>
+
+<h2><&|/l&>Topics</&></h2>
+<& Elements/ShowTopics, article => $article &>
+<%init>
+
+my $article = RT::FM::Article->new( $session{'CurrentUser'} );
+if ($Name) {
+    $article->LoadByCols( Name => $Name );
+}
+elsif ($id) {
+    $article->Load($id);
+}
+unless ( $article->Id ) {
+    if ( $ARGS{'Name'} ) {
+        $m->comp( 'Edit.html', %ARGS );
+        return ();
+    }
+    else {
+        $m->comp( "/Elements/Error", Why => loc("Article not found") );
+    }
+
+}
+
+unless ( $article->CurrentUserHasRight('ShowArticle') ) {
+    $m->comp( "/SelfService/RTFM/Elements/Error", Why => loc("Permission Denied") );
+}
+my $title = loc( "Article #[_1]: [_2]", $article->Id, $article->Name || loc("(no name)"));
+
+$id = $article->id;
+
+</%init>
+<%args>
+$id => undef
+$Name => undef
+</%args>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/LinkEntryInstructions
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/LinkEntryInstructions	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,2 @@
+<&|/l&>Type <b>a:</b> before article numbers and <b>t:</b> before ticket numbers.</&>
+<&|/l&>Separate multiple entries with spaces.</&>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/SearchByCustomField
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/SearchByCustomField	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,40 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+
+%# if the custom field is a select, enumerate the options
+% if ($Field->Type =~ /^Select/) {
+% my $CustomFieldValues = $Field->ValuesObj();
+<select name="<%$Name%>" size="5" multiple>
+% while (my $value = $CustomFieldValues->Next) {
+<option value="<%$value->Name%>" <% grep (/^@{[$value->Name]}$/, @Values) && 'SELECTED'%>><% $value->Name%></option>
+% }
+<option value="" <% $#Values < 0 && 'SELECTED'%>><&|/l&>(no value)</&></option>
+</select>
+% }
+%# otherwise, put in a textedity field
+% else {
+<input name="<%$Name%>" value="<%$Values[0]%>" />
+% }
+<%init>
+my @Values =ref( $Values ) ? @{ $Values } : ( $Values ); 
+</%init>
+<%ARGS>
+$Field => undef
+$Name => 'CustomField'
+$Values => undef
+</%ARGS>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/SelectSavedSearches
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/SelectSavedSearches	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,74 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%#  
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC 
+%#                                          <jesse at bestpractical.com>
+%# 
+%# (Except where explicitly superseded by other copyright notices)
+%# 
+%# 
+%# LICENSE:
+%# 
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%# 
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%# 
+%# 
+%# CONTRIBUTION SUBMISSION POLICY:
+%# 
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%# 
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%# 
+%# END BPS TAGGED BLOCK }}}
+<select name="<%$Name%>">
+% foreach my $privacy (reverse sort keys %privacies) {
+%     my $searches = RT::SavedSearches->new($session{'CurrentUser'});
+%     $searches->LimitToPrivacy($privacy, 'Article');
+%     next unless $searches->Count;
+%     if ($privacy =~ /^RT::User/) {
+<option value=""><&|/l&>My saved searches</&></option>
+%     } else {
+<option value=""><&|/l, $privacies{$privacy}->Name&>[_1]'s saved searches</&></option>
+%     }
+%     while (my $search = $searches->Next) {
+%          my $optionval = "$privacy-SavedSearch-". $search->Id;
+<option value="<%$optionval%>"<% $optionval eq $Default ? 'selected' : '' %>> - <%$search->Name||loc('Unnamed search')%></option>
+%     }
+% }
+</select>
+
+<%init>
+use RT::SavedSearches;
+my $groups = $session{'CurrentUser'}->UserObj->OwnGroups;
+my %privacies;
+$privacies{'RT::User-' . $session{'CurrentUser'}->UserObj->Id} = 1;
+map { $privacies{'RT::Group-'.$_->Id} = $_ } @{$groups->ItemsArrayRef};
+</%init>
+
+<%args>
+$Name => undef
+$Default => undef
+</%args>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowLinks
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowLinks	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,64 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+
+
+<span class="label"><&|/l&>Refers to</&></span>:<br />
+<ul class="value">
+% my $refersto = $article->RefersTo;
+
+% while (my $link = $refersto->Next) {
+% my $member = $link->TargetURI;
+<li>
+% if ($link->TargetURI->IsLocal) {
+<a href="<%$member->Resolver->HREF%>"><% loc($member->Object->ObjectTypeStr) %> <%$member->Object->Id%>: 
+% if (UNIVERSAL::isa($member->Object, "RT::FM::Article") or UNIVERSAL::can($member->Object, 'Name')) {
+<%$member->Object->Name%>
+% } elsif (UNIVERSAL::isa($member->Object, "RT::Ticket") or UNIVERSAL::can($member->Object, 'Subject')) {
+<%$member->Object->Subject%>
+% }
+</a>
+% } else {
+<a href="<%$member->Resolver->HREF%>"><%$link->Target%></a>
+% }
+</li>
+% }
+</ul>
+
+<span class="label"><&|/l&>Referred to by</&></span>:<br />
+<ul class="value">
+% my $referredtoby = $article->ReferredToBy;
+% while (my $link = $referredtoby->Next) {
+% my $member = $link->BaseURI;
+<li>
+% if ($member->IsLocal) {
+<a href="<%$member->Resolver->HREF%>"><% loc($member->Object->ObjectTypeStr) %> <%$member->Object->Id%>: 
+% if (UNIVERSAL::isa($member->Object, "RT::FM::Article") or UNIVERSAL::can($member->Object, 'Name')) {
+<%$member->Object->Name%>
+% } elsif (UNIVERSAL::isa($member->Object, "RT::Ticket") or UNIVERSAL::can($member->Object, 'Subject')) {
+<%$member->Object->Subject%>
+% }
+</a>
+% } else {
+<a href="<%$member->Resolver->HREF%>"><%$link->Base%></a>
+% }
+</li>
+% }
+</ul>
+<%args>
+$article => undef
+</%args>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowSavedSearches
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowSavedSearches	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,83 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%#  
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC 
+%#                                          <jesse at bestpractical.com>
+%# 
+%# (Except where explicitly superseded by other copyright notices)
+%# 
+%# 
+%# LICENSE:
+%# 
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%# 
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%# 
+%# 
+%# CONTRIBUTION SUBMISSION POLICY:
+%# 
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%# 
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%# 
+%# END BPS TAGGED BLOCK }}}
+<& /Elements/TitleBoxStart, title => loc('Saved searches') &>
+%# Keep track of what our current search ID is.
+<input type="hidden" name="CurrentSearch" value="<% $CurrentSearch ? $CurrentSearch : 'new' %>">
+%# Hide all the save functionality if the user shouldn't see it.
+% if ($session{'CurrentUser'}->HasRight( Right => 'CreateSavedSearch',
+%                                       Object=> $RT::System )) {
+<h2><&|/l&>Save this search</&></h2>
+<&|/l&>Name:</&> <input name="NewSearchName" value="<%$Name%>">
+<&|/l&>Privacy:</&> <& SelectSearchPrivacy, Name => 'SearchPrivacy',
+	Default => $Privacy &><br />
+%     if ($CurrentSearch && $CurrentSearch ne 'new') {
+<input value="<%loc('Update')%>" name="Update" type="submit" />&nbsp;
+<input value="<%loc('Save new')%>" name="Save" type="submit" />&nbsp;
+<input value="<%loc('Delete')%>" name="Delete" type="submit" />&nbsp;
+%     } else {
+<input value="<%loc('Save')%>" name="Save" type="submit" />
+%     }
+<hr />
+% } # if HasRight 'CreateSavedSearch'
+
+<h2><&|/l&>Load a saved search</&></h2>
+<& SelectSavedSearches, Name => 'LoadSavedSearch', Default => $CurrentSearch &>
+<input value="<%loc('Load')%>" name="Load" type="submit">
+<& /Elements/TitleBoxEnd &>
+
+<%INIT>
+unless ($session{'CurrentUser'}->HasRight( Right => 'LoadSavedSearch',
+                                          Object=> $RT::System )) {
+    return;
+}
+
+</%INIT>
+
+<%ARGS>
+$CurrentSearch => undef
+$Name => undef
+$Privacy => undef
+</%ARGS>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowSearchCriteria
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowSearchCriteria	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,133 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+
+% if ($ARGS{'HideOptions'}) {
+<& /Elements/TitleBoxStart, title => loc('Advanced search'), class => "rolled-up", bodyclass => "hidden" &>
+% } else {
+<& /Elements/TitleBoxStart, title => loc('Advanced search') &>
+% }
+<table>
+<tr>
+<td class="label" colspan="2"><h2><&|/l&>Basics</&></h2></td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Class</&></td><td class="value">is <& /SelfService/RTFM/Elements/SelectClass, Name => 'Class', Multiple =>1, Size => 5 , ShowNullOption => undef,  Default => $ARGS{'Class'} &> 
+<&|/l&>and not</&>
+<& /SelfService/RTFM/Elements/SelectClass, Name => 'Class!', Multiple =>1, Size => 5 , ShowNullOption => undef, Default => $ARGS{'Class!'} &></td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Name</&></td>
+<td class="value"><&|/l&>matches</&> <input name="Name~" value="<%$ARGS{'Name~'}%>" /> <&|/l&>and not</&> <input name="Name!~" value="<%$ARGS{'Name!~'}%>" /></td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Summary</&></td>
+<td class="value"><&|/l&>matches</&> <input name="Summary~" value="<%$ARGS{'Summary~'}%>" /> <&|/l&>and not</&> <input name="Summary!~" value="<%$ARGS{'Summary!~'}%>" /></td>
+</tr>
+<tr>
+<td class="label" colspan="2"><h2><&|/l&>Content</&></h2></td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Any field</&></td><td class="value"> <&|/l&>matches</&> <input name="Article~" value="<%$ARGS{'Article~'}%>" /> <&|/l&>and not</&> <input name="Article!~" value="<%$ARGS{'Article!~'}%>" /></td>
+</tr>
+% while (my $field = $customfields->Next ) {
+<tr>
+<td class="label"><% $field->Name %></td>
+<td class="value"><&|/l&>matches</&> 
+% my $matches = $field->Name."~";
+% my $nomatches = $field->Name."!~";
+<& /SelfService/RTFM/Article/Elements/SearchByCustomField, 
+    Field => $field, 
+    Name => $matches,
+    Values => $ARGS{$matches} &>
+    <&|/l&>and not</&>
+<& /SelfService/RTFM/Article/Elements/SearchByCustomField, 
+    Field => $field, 
+    Name => $nomatches,
+    Values => $ARGS{$nomatches}
+    &>
+</td>
+</tr>
+% }
+<tr>
+<td class="label" colspan="2"><h2><&|/l&>Dates</&></h2></td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Created</&></td>
+<td class="value"><&|/l&>after</&>
+<& /Elements/SelectDate, Name=>"Created>", Default => ($dates->{'Created>'} ? $dates->{'Created>'}->ISO : '') &>
+<&|/l&>and before</&>
+<& /Elements/SelectDate, Name=>"Created<", Default => ($dates->{'Created<'} ? $dates->{'Created<'}->ISO:'')&>
+</td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Last updated</&></td>
+<td class="value"><&|/l&>after</&>
+<& /Elements/SelectDate, Name=>"LastUpdated>", Default =>
+($dates->{'LastUpdated>'} ? $dates->{'LastUpdated>'}->AsString:'')&>
+<&|/l&>and before</&>
+<& /Elements/SelectDate, Name=>"LastUpdated<", Default => 
+($dates->{'LastUpdated<'} ? $dates->{'LastUpdated<'}->AsString:'')&>
+</td>
+</tr>
+<tr>
+<td class="label" colspan="2"><h2><&|/l&>Links</&></h2></td>
+</tr>
+<tr>
+<td class="label"></td>
+<td><& LinkEntryInstructions &></td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Refer to</&></td>
+<td class="value"><input type=text size=50 name="RefersTo" value="<%$RefersTo%>" /></td>
+</tr>
+<tr>
+<td class="label"><&|/l&>Referred to by</&></td>
+<td class="value"><input type=text size=50 name="ReferredToBy" value="<%$ReferredToBy%>" /></td>
+</tr>
+<tr>
+<td class="label" colspan="2"><h2><&|/l&>Topics</&></h2></td>
+</tr>
+<tr>
+<td class="label"></td>
+<td>
+<br />
+<input type="checkbox" name="ExpandTopics" <% $ARGS{'ExpandTopics'} ? 'checked="checked"' : "" %> />
+<&|/l&>Include subtopics</&> 
+</td>
+</tr>
+</table>
+<& /Elements/Submit, Label => loc('Search') &>
+<&/Elements/TitleBoxEnd&>
+<%init>
+my @Classes =
+ ( ref $ARGS{'Class'} eq 'ARRAY' )
+      ? @{ $ARGS{'Class'} }
+      : ( $ARGS{'Class'} );
+for (@Classes) {
+  my $class = RT::FM::Class->new( $session{'CurrentUser'} );
+  $class->LoadById($_);
+  $_ = $class;
+}
+</%init>
+
+<%ARGS>
+$dates =>undef
+$RefersTo => undef
+$ReferredToBy => undef
+$customfields => undef
+</%ARGS>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowSearchResults
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowSearchResults	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,101 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+
+% # THIS IS A HORRIBLE HACK TO SEE IF WE TRIED TO SEARCH
+% # IT CAN GO AWAY WHEN SEARCHBUILDER'S _isLimited IS 
+% # PROMOTED TO A PUBLIC API XXX TODO
+% if ($articles->BuildSelectCountQuery =~ /WHERE/i) {
+<h2><&|/l&>Search results</&></h2>
+% if ($articles->Count){
+
+<table width="100%" border="0" cellpadding="2" cellspacing="0">
+<tr class="collection-as-table">
+<th style="text-align: left" class="collection-as-table"><% $sort_link->( loc('id'), 'id') |n %></th>
+<th style="text-align: left" class="collection-as-table"><% $sort_link->( loc('Name'), 'Name') |n %></th>
+<th style="text-align: left" class="collection-as-table"><&|/l&>Class</&></th>
+<th style="text-align: left" class="collection-as-table"><% $sort_link->( loc('Created'), 'Created') |n %></th>
+<th style="text-align: left" class="collection-as-table"><% $sort_link->( loc('Modified'), 'LastUpdated') |n %></th>
+</tr>
+<tr class="collection-as-table">
+<th class="collection-as-table"></th>
+<th style="text-align: left" class="collection-as-table"><% $sort_link->( loc('Summary'), 'Summary') |n %></th>
+<th style="text-align: left" class="collection-as-table" colspan="3"><&|/l&>Topics</&></th>
+</tr>
+
+% while (my $article = $articles->Next) {
+%$i++;
+<tr \
+% if ($i%2) {
+class="oddline" \
+% } else {
+class="evenline" \
+% }
+>
+<td rowspan="2"><b><a href="Display.html?id=<%$article->Id%>">#<%$article->id%></a></b></td>
+<td>            <b><a href="Display.html?id=<%$article->Id%>"><%$article->Name || loc('(no name)')%></a></b></td>
+<td>            <%$article->ClassObj->Name%></td>
+<td><small>     <%$article->CreatedObj->AgeAsString || '-'%></small></td>
+<td><small>     <%$article->LastUpdatedObj->AgeAsString || '-'%></small></td>
+</tr>
+<tr \
+% if ($i%2) {
+class="oddline" \
+% } else {
+class="evenline" \
+% }
+>
+<td><small>     <%$article->Summary%></small></td>
+<td colspan="3"><small>
+% my $Topics = $article->Topics;
+% while (my $t = $Topics->Next) {
+<& /SelfService/RTFM/Elements/ShowTopic, topic => $t->TopicObj &><br />
+% }
+</small></td>
+</tr>
+% }
+</table>
+% }
+% else {
+<i><&|/l&>No articles found.</&></i>
+% }
+% }
+<%init>
+my $i;
+
+my @OrderBy = $query{'OrderBy'}? @{ $query{'OrderBy'} }: ();
+my @Order = $query{'Order'}? @{ $query{'Order'} }: ();
+
+my $sort_link = sub {
+    my ($title, $column) = @_;
+    my $res = qq{<a href="$RT::WebPath/SelfService/RTFM/Article/Search.html?};
+    my $order;
+    for ( grep $OrderBy[$_] eq $column, 0..$#OrderBy ) {
+        $order = 'DESC' if !$Order[$_] || $Order[$_] =~ /^ASC/i;
+    }
+    $res .= $m->comp('/Elements/QueryString', %query, OrderBy => $column, Order => ($order || 'ASC'));
+
+    $res .= q{">} . $m->interp->apply_escapes($title, 'h')
+         . q{</a>};
+    return $res;
+};
+</%init>
+<%args>
+$articles => undef
+$did_search => 1
+%query => ()
+</%args>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowTopics
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Elements/ShowTopics	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,28 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+% my $topics = new RT::FM::ObjectTopicCollection($session{'CurrentUser'});
+% $topics->LimitToObject($article);
+% my @topics;
+% while (my $t = $topics->Next) {
+<& /SelfService/RTFM/Elements/ShowTopic, topic => $t->TopicObj &><br />
+% }
+<br />
+
+<%args>
+$article => undef
+</%args>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Search.html
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Article/Search.html	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,482 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+
+<& /SelfService/Elements/Header, Title => loc('Search Articles') &>
+
+% unless ( keys %ARGS ) {
+%   my $Classes=new RT::FM::ClassCollection($session{'CurrentUser'});
+%   $Classes->LimitToEnabled();
+<table width="100%" border="0">
+<tr>
+<td valign="top" width="50%">
+<ul>
+% while (my $class = $Classes->Next) {
+<li><a href="<%$RT::WebPath%>/SelfService/RTFM/Article/Search.html?<% $m->comp('/Elements/QueryString', %filtered, Class => $class->id) %>"><&|/l, $class->Name&>in class [_1]</&></a></li>
+% }  
+</ul>
+</td>
+<td valign="top" width="50%">
+<form action="Search.html" method="get">
+<& /Elements/TitleBoxStart, title => loc('Saved searches') &>
+<&|/l&>Load saved search:</&><br />
+<& Elements/SelectSavedSearches, Name => 'LoadSavedSearch', Default => $CurrentSearch &>
+<input value="<%loc('Load')%>" name="Load" type="submit" />
+<& /Elements/TitleBoxEnd &>
+</form>
+</td>
+</tr>
+</table>
+%  return;
+% }
+
+<& /Elements/ListActions, actions => \@results &>
+<div style="float: right"><a href="#criteria"><&|/l&>Modify this search...</&></a></div>
+
+<& Elements/ShowSearchResults, query => \%filtered, articles => $articles &>
+
+<br />
+<br />
+<br />
+<a name="criteria"></a>
+<form action="Search.html" method="get">
+<& Elements/ShowSearchCriteria, dates => \%dates, RefersTo => $RefersTo, customfields => $customfields,  ReferredToBy => $ReferredToBy, %ARGS &>
+<br />
+<br />
+<& Elements/ShowSavedSearches, CurrentSearch => $CurrentSearch, 
+    Name => ($search ? $search->Name : undef),
+    Privacy => ($search ? $search->Privacy : undef) &>
+
+</form>
+
+<div align=right>
+<a href="<%$RT::WebPath%>/SelfService/RTFM/Article/Search.html<%$QueryString%>"><&|/l&>Bookmarkable link for this search</&></a><br />
+</div>
+<%init>
+use RT::SavedSearch;
+my @results;
+my $articles = RT::FM::ArticleCollection->new( $session{'CurrentUser'} );
+
+# {{{ Quicksearch logic
+
+# If it is a number, load the article with that ID.  Otherwise, search
+# on name and summary.
+if ($ARGS{'q'} =~ /^(\d+)$/) {
+    return $m->comp("/SelfService/RTFM/Article/Display.html", id => $1);
+} elsif ($ARGS{'q'}) {
+    $articles->Limit( FIELD => 'Name',
+                      SUBCLAUSE => 'NameOrSummary',
+                      OPERATOR => 'LIKE',
+                      ENTRYAGGREGATOR => 'OR',
+                      CASESENSITIVE => 0,
+                      VALUE => $ARGS{'q'} );
+    $articles->Limit( FIELD => 'Summary',
+                      SUBCLAUSE => 'NameOrSummary',
+                      OPERATOR => 'LIKE',
+                      ENTRYAGGREGATOR => 'OR',
+                      CASESENSITIVE => 0,
+                      VALUE => $ARGS{'q'} );
+}
+
+# }}}
+
+# {{{ Saved search logic
+
+my $search;
+
+# The keys in %ARGS that are not saved and loaded with named searches.
+# These need to be treated specially.
+my @metakeys = qw/NewSearchName CurrentSearch SearchPrivacy Save Load
+    Update Delete/;
+
+if ($CurrentSearch =~ /^(.*-\d+)-SavedSearch-(\d+)$/) {
+    $search = RT::SavedSearch->new($session{'CurrentUser'});
+    $search->Load($1, $2);
+}
+
+# Have we been asked to load a search?
+
+if ($ARGS{'Load'}) {
+    if ($ARGS{'LoadSavedSearch'} =~ /^(.*-\d+)-SavedSearch-(\d+)$/ ) {
+	my $privacy = $1;
+	my $search_id = $2;
+	
+	$search = RT::SavedSearch->new($session{'CurrentUser'});
+	my ($ret, $msg) = $search->Load($privacy, $search_id);
+	if ($ret) {
+	    my $searchargs = $search->GetParameter('args');
+	    # Clean out ARGS and fill it in with the saved args from the 
+	    # loaded search.
+	    foreach my $key (@metakeys) {
+		$searchargs->{$key} = $ARGS{$key};
+	    }
+	    %ARGS = %{$searchargs};
+	    $CurrentSearch = "$privacy-SavedSearch-$search_id";
+	} else {
+	    push(@results, loc("Error: could not load saved search [_1]: [_2]",
+			       $ARGS{'LoadSavedSearch'}, $msg));
+	}
+    } else {
+	push(@results, loc("Invalid [_1] argument", 'LoadSavedSearch'));
+    }
+}
+
+# ...or have we been asked to save, update, or delete a search?
+
+if ($ARGS{'Save'}) {
+    my %searchargs = %ARGS;
+    foreach my $key (@metakeys) {
+	delete $searchargs{$key};
+    }
+
+    $search = RT::SavedSearch->new($session{'CurrentUser'});
+    unless ($ARGS{'SearchPrivacy'} =~ /^(.*)-(\d+)$/) {
+	# This shouldn't really happen, but hey.
+	push(@results, loc("WARNING: Saving search to user-level privacy"));
+	$ARGS{'SearchPrivacy'} = 'RT::User-'.$session{'CurrentUser'}->Id;
+    }
+    my ($ret, $msg) = $search->Save(Privacy => $ARGS{'SearchPrivacy'}, 
+				    Type => 'Article',
+				    Name => $ARGS{'NewSearchName'}, 
+				    SearchParams => {'args' => \%searchargs});
+    if ($ret) {
+	$CurrentSearch = $ARGS{'SearchPrivacy'} . "-SavedSearch-" . 
+	    $search->Id;
+	push(@results, loc("Created search [_1]", $search->Name));
+    } else {
+        undef $search; # if we bomb out creating a search
+                        # we don't want to have the empty object hang around
+	push(@results, loc("Could not create search: [_1]", $msg));
+    }
+} elsif ($ARGS{'Update'}) {
+    if ($ARGS{'SearchPrivacy'} != $search->Privacy) {
+	push(@results, 
+	     loc("Error: cannot change privacy value of existing search"));
+    } else {
+	my %searchargs = %ARGS;
+	foreach my $key (@metakeys) {
+	    delete $searchargs{$key};
+	}
+	
+	# We already have a search loaded, because CurrentSearch is set,
+	# or else we would not have gotten here.
+	my ($ret, $msg) = $search->Update(Name => $ARGS{'NewSearchName'},
+					  SearchParams => \%searchargs);
+	if ($ret) {
+	    push(@results, loc("Search [_1] updated", $search->Name));
+	} else {
+	    push(@results, loc("Error: search [_1] not updated: [_2]",
+			       $search->Name, $msg));
+	}
+    }
+} elsif ($ARGS{'Delete'}) {
+    # Keep track of this, as we are about to delete the search.
+    my $searchname = $search->Name;
+    my ($ret, $msg) = $search->Delete;
+    if ($ret) {
+	$ARGS{'CurrentSearch'} = undef;
+	push(@results, loc("Deleted search [_1]", $searchname));
+	# Get rid of all the state.
+	foreach my $key (keys %ARGS) {
+	    delete $ARGS{$key};
+	}
+	$CurrentSearch = 'new';
+	$search = undef;
+	$RefersTo = undef;
+	$ReferredToBy = undef;
+    } else {
+	push(@results, loc("Could not delete search [_1]: [_2]", 
+	     $searchname, $msg));
+    }
+}
+
+# }}}
+
+
+# Don't want to search for a null class when there is no class specced
+my $customfields = RT::CustomFields->new( $session{'CurrentUser'} );
+$customfields->LimitToLookupType(RT::FM::Article->new($session{'CurrentUser'})->CustomFieldLookupType);
+if ( $ARGS{'Class'} ) {
+    my @Classes =
+      ( ref $ARGS{'Class'} eq 'ARRAY' )
+      ? @{ $ARGS{'Class'} }
+      : ( $ARGS{'Class'} );
+    foreach my $class (@Classes) {
+        $customfields->LimitToGlobalOrObjectId($class);
+    }
+}
+else {
+    $customfields->LimitToGlobalOrObjectId();
+}
+
+my %dates;
+foreach my $date qw(Created< Created> LastUpdated< LastUpdated>) {
+    next unless ( $ARGS{$date} );
+    my $seconds = parsedate( $ARGS{$date}, FUZZY => 1, PREFER_PAST => 1 );
+    my $date_obj = RT::Date->new( $session{'CurrentUser'} );
+    $date_obj->Set( Format => 'unix', Value => $seconds );
+    $dates{$date} = $date_obj;
+
+    if ( $date =~ /^(.*?)<$/i ) {
+        $articles->Limit( FIELD           => $1, OPERATOR        => "<=", ENTRYAGGREGATOR => "AND", VALUE           => $date_obj->ISO );
+    }
+
+    if ( $date =~ /^(.*?)>$/i ) {
+        $articles->Limit( FIELD           => $1, OPERATOR        => ">=", ENTRYAGGREGATOR => "AND", VALUE           => $date_obj->ISO );
+    }
+
+}
+foreach my $link ( split ( /\s+/, $RefersTo ) ) {
+    next unless ($link);
+    $articles->LimitRefersTo($link);
+}
+
+foreach my $link ( split ( /\s+/, $ReferredToBy ) ) {
+    next unless ($link);
+    $articles->LimitReferredToBy($link);
+}
+
+if ($ARGS{'Topics'}) {
+    my @Topics =
+      ( ref $ARGS{'Topics'} eq 'ARRAY' )
+      ? @{ $ARGS{'Topics'} }
+      : ( $ARGS{'Topics'} );
+    @Topics = map {split} @Topics;
+    if ($ARGS{'ExpandTopics'}) {
+        my %topics;
+        while (@Topics) {
+            my $id = shift @Topics;
+            next if $topics{$id};
+            my $Topics = RT::FM::TopicCollection->new($session{'CurrentUser'});
+            $Topics->Limit(FIELD => 'Parent', VALUE => $id);
+            push @Topics, $_->Id while $_ = $Topics->Next;
+            $topics{$id}++;
+        }
+        @Topics = keys %topics;
+        $ARGS{'Topics'} = \@Topics;
+    }
+    $articles->LimitTopics(@Topics);
+}
+
+my %cfs;
+my $all_cfs = RT::CustomFields->new($session{'CurrentUser'});
+$all_cfs->UnLimit();
+while ( my $cf = $all_cfs->Next ) {
+    $cfs{ $cf->Name } = $cf->Id;
+}
+
+foreach my $field ( keys %cfs ) {
+   
+   my @MatchLike = (ref $ARGS{ $field."~" } eq 'ARRAY' ) ? @{ $ARGS{ $field."~" } } : ( $ARGS{$field."~" } );
+   my @NoMatchLike = (ref $ARGS{ $field."!~" } eq 'ARRAY' ) ? @{ $ARGS{ $field."!~" } } : ( $ARGS{$field."!~" } );
+
+   my @Match = (ref $ARGS{ $field } eq 'ARRAY' ) ? @{ $ARGS{ $field } } : ( $ARGS{$field } );
+   my @NoMatch = (ref $ARGS{ $field."!" } eq 'ARRAY' ) ? @{ $ARGS{ $field."!" } } : ( $ARGS{$field."!" } );
+
+    foreach my $val (@MatchLike) {
+        next unless $val;
+        push @Match, "~".$val;
+    }
+
+    foreach my $val (@NoMatchLike) {
+        next unless $val;
+        push @NoMatch, "~".$val;
+    }
+
+
+    foreach my $value (@Match) {
+        next unless $value;
+        my $op;
+        if ( $value =~ /^~(.*)$/ ) {
+            $value = "%$1%";
+            $op    = 'LIKE';
+        }
+        else {
+            $op = '=';
+        }
+        $articles->LimitCustomField( FIELD           => $cfs{$field},
+                                     VALUE           => $value,
+                                     CASESENSITIVE   => 0,
+                                     ENTRYAGGREGATOR => 'OR',
+                                     OPERATOR        => $op );
+    }
+    foreach my $value (@NoMatch) {
+        next unless $value;
+        my $op;
+        if ( $value =~ /^~(.*)$/ ) {
+            $value = "%$1%";
+            $op    = 'NOT LIKE';
+        }
+        else {
+            $op = '!=';
+        }
+        $articles->LimitCustomField( FIELD           => $cfs{$field},
+                                     VALUE           => $value,
+                                     CASESENSITIVE   => 0,
+                                     ENTRYAGGREGATOR => 'OR',
+                                     OPERATOR        => $op );
+    }
+}
+
+### Searches for any field
+
+if ($ARGS{'Article~'}) {
+    $articles->LimitCustomField( VALUE => $ARGS{'Article~'},
+                                 ENTRYAGGREGATOR => 'OR',
+                                 OPERATOR => 'LIKE',
+                                 CASESENSITIVE => 0,
+                                 SUBCLAUSE => 'SearchAll' );
+    $articles->Limit( SUBCLAUSE => 'SearchAll',
+                      FIELD => "Name",
+                      VALUE => $ARGS{'Article~'},
+                      ENTRYAGGREGATOR => 'OR',
+                      CASESENSITIVE => 0,
+                      OPERATOR => 'LIKE' );
+    $articles->Limit( SUBCLAUSE => 'SearchAll',
+                      FIELD => "Summary",
+                      VALUE => $ARGS{'Article~'},
+                      ENTRYAGGREGATOR => 'OR',
+                      CASESENSITIVE => 0,
+                      OPERATOR => 'LIKE' );
+}
+
+if ($ARGS{'Article!~'}) {
+    $articles->LimitCustomField( VALUE => $ARGS{'Article!~'},
+                                 OPERATOR => 'NOT LIKE',
+                                 CASESENSITIVE => 0,
+                                 SUBCLAUSE => 'SearchAll' );
+    $articles->Limit( SUBCLAUSE => 'SearchAll',
+                      FIELD => "Name",
+                      VALUE => $ARGS{'Article!~'},
+                      ENTRYAGGREGATOR => 'AND',
+                      CASESENSITIVE => 0,
+                      OPERATOR => 'NOT LIKE' );
+    $articles->Limit( SUBCLAUSE => 'SearchAll',
+                      FIELD => "Summary",
+                      VALUE => $ARGS{'Article!~'},
+                      ENTRYAGGREGATOR => 'AND',
+                      CASESENSITIVE => 0,
+                      OPERATOR => 'NOT LIKE' );
+}
+
+foreach my $field qw(Name Summary Class) {
+
+   my @MatchLike = (ref $ARGS{ $field."~" } eq 'ARRAY' ) ? @{ $ARGS{ $field."~" } } : ( $ARGS{$field."~" } );
+   my @NoMatchLike = (ref $ARGS{ $field."!~" } eq 'ARRAY' ) ? @{ $ARGS{ $field."!~" } } : ( $ARGS{$field."!~" } );
+
+   my @Match = (ref $ARGS{ $field } eq 'ARRAY' ) ? @{ $ARGS{ $field } } : ( $ARGS{$field } );
+   my @NoMatch = (ref $ARGS{ $field."!" } eq 'ARRAY' ) ? @{ $ARGS{ $field."!" } } : ( $ARGS{$field."!" } );
+
+    foreach my $val (@MatchLike) {
+        next unless $val;
+        push @Match, "~".$val;
+    }
+
+    foreach my $val (@NoMatchLike) {
+        next unless $val;
+        push @NoMatch, "~".$val;
+    }
+
+    my $op;
+    foreach my $value (@Match) {
+        if ( $value =~ /^~(.*)$/ ) {
+            $value = "%$1%";
+            $op    = 'LIKE';
+        }
+        else {
+            $op = '=';
+        }
+
+        # preprocess Classes, so we can search on class
+        if ( $field eq 'Class' && $value ) {
+            my $class = RT::FM::Class->new($RT::SystemUser);
+            $class->Load($value);
+            $value = $class->Id;
+        }
+
+        # now that we've pruned the value, get out if it's different.
+        next unless $value;
+
+        $articles->Limit( SUBCLAUSE       => $field . 'Match',
+                          FIELD           => $field,
+                          OPERATOR        => $op,
+                          CASESENSITIVE   => 0,
+                          VALUE           => $value,
+                          ENTRYAGGREGATOR => 'OR' );
+
+    }
+    foreach my $value (@NoMatch) {
+
+        # preprocess Classes, so we can search on class
+        if ( $value =~ /^~(.*)/ ) {
+            $value = "%$1%";
+            $op    = 'NOT LIKE';
+        }
+        else {
+            $op = '!=';
+        }
+        if ( $field eq 'Class' ) {
+            my $class = RT::FM::Class->new($RT::SystemUser);
+            $class->Load($value);
+            $value = $class->Id;
+        }
+
+        # now that we've pruned the value, get out if it's different.
+        next unless $value;
+
+        $articles->Limit( SUBCLAUSE       => $field . 'NoMatch',
+                          OPERATOR        => $op,
+                          VALUE           => $value,
+                          CASESENSITIVE   => 0,
+                          FIELD           => $field,
+                          ENTRYAGGREGATOR => 'AND' );
+
+    }
+}
+
+if ( @OrderBy ) {
+    if ( $OrderBy[0] && $OrderBy[0] =~ /\|/ ) {
+        @OrderBy = split '|', @OrderBy;
+        @Order   = split '|', @Order;
+    }
+    my @tmp = map
+        {{ FIELD => $OrderBy[$_], ORDER => $Order[$_] }}
+        0..$#OrderBy;
+    $articles->OrderByCols( @tmp );
+}
+
+
+$m->comp('/Elements/Callback', %ARGS, _Search => $articles);
+
+my %filtered = %ARGS;
+delete $filtered{$_} for (@metakeys, "EditTopics", "ExpandTopics");
+delete $filtered{$_} for grep {$filtered{$_} !~ /\S/} keys %filtered;
+ at filtered{qw(OrderBy Order)} = (\@OrderBy, \@Order);
+my $QueryString = "?".$m->comp('/Elements/QueryString', %filtered);
+</%init>
+
+<%ARGS>
+$CreatedBefore => ''
+$CreatedAfter => ''
+$LastUpdatedBefore => ''
+$LastUpdatedAfter => ''
+$RefersTo => undef
+$ReferredToBy => undef
+$CurrentSearch => 'new'
+ at OrderBy => ()
+ at Order   => ()
+</%ARGS>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/Error
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/Error	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,56 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+
+<& /Elements/TitleBoxStart, class=> "error",  title => $Title &>
+<%$Why%>
+<br />
+<font size=-1>
+<%$Details%>
+</font>
+<& /Elements/TitleBoxEnd &>
+</body>
+</html>
+
+<%perl>
+$m->abort();
+</%perl>
+<%args>
+$Code => undef
+$Details => undef
+$Title => loc("RTFM Error")
+$Why => loc("the calling component did not specify why")
+</%args>
+
+<%INIT>
+my $error = "WebRT: $Why ($Details)";
+
+# TODO: Log::Dispatch isn't UTF-8 safe. Autrijus needs to talk to dave rolsky about getting this fixed
+if ($] >= 5.007001) {
+    require Encode;
+    Encode::_utf8_off($error);
+}
+
+$RT::Logger->error($error);
+
+if ( $session{'SessionType'} eq 'REST' ) {
+    $r->content_type('text/plain');
+    $m->out( "Error: " . $Why . "\n" );
+    $m->out( $Details . "\n" );
+    $m->abort();
+}
+</%INIT>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/GotoArticle
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/GotoArticle	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,44 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+
+<form ACTION="<%$RT::WebPath%>/SelfService/RTFM/Article/Search.html">
+<input type="hidden" name="HideOptions" value="1" />
+<input size="12" name="q" accesskey="0" />
+% if ($class or $topic) {
+<input type="hidden" name="ExpandTopics" value="1" />
+<select name="Topics">
+% if ($topic) {
+<option value="<% $topic %>">in this topic</option>
+% }
+% if ($class) {
+% my $Topics = RT::FM::TopicCollection->new($session{'CurrentUser'});
+% $Topics->Limit(FIELD => 'ObjectType', VALUE => 'RT::FM::Class');
+% $Topics->Limit(FIELD => 'ObjectId',   VALUE => $class);
+% my @topics;
+% push @topics, $_->Id while $_ = $Topics->Next;
+<option value="<% join(' ', @topics) %>">in this hierarchy</option>
+% }
+<option value="">in all topics</option>
+</select>
+% }
+<input type="submit" value="<&|/l&>Search RTFM</&>" />&nbsp;
+</form>
+<%args>
+$topic => ""
+$class => ""
+</%args>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/Header
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/Header	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,18 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+<& /Elements/Header, %ARGS &>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/QuickSearch
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/QuickSearch	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,29 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+<& /Elements/TitleBoxStart, title => loc('Quick search') &>
+<ul>
+% while (my $class = $classes->Next) {
+<li><a href="<%$RT::WebPath%>/SelfService/RTFM/Article/Search.html?Class=<%$class->Name%>&Parent=0&HideOptions=1"><%$class->Name%></a></li>
+% }
+</ul>
+<& /Elements/TitleBoxEnd &>
+<%init>
+my $classes = RT::FM::ClassCollection->new($session{'CurrentUser'});
+$classes->LimitToEnabled;
+</%init>
+

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/SelectClass
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/SelectClass	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,66 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+
+% if ($Lite) {
+<input name="<%$Name%>" size="25" default="<%$d->Name%>" />
+% } else {
+<select NAME ="<%$Name%>"
+% if ($Multiple) {
+MULTIPLE
+% }
+% if ($Size) {
+SIZE=<%$Size%>
+% }
+>
+% if ($ShowNullOption) {
+<option value="">-</option>
+% }
+% while (my $Class=$Classes->Next) {
+% next unless ($Class->Name); # if they can't see it, don't list it
+% if ($ShowAllClasses || $Class->CurrentUserHasRight('CreateArticle')) {
+<option VALUE="<%$Class->Id%>" <%(grep { $_ && ( $Class->Id == $_ || $Class->Name eq $_ )} @Default) ? 'SELECTED' : '' %>><%$Class->Name%>
+%   if (($Verbose) and ($Class->Description) ){
+(<%$Class->Description%>)
+%  }
+</option>
+% }
+% }
+</select>
+% }
+<%ARGS>
+$Multiple => undef
+$Size => undef
+$ShowNullOption => 1
+$ShowAllClasses => 1
+$Name => undef
+$Verbose => undef
+$Default => undef
+$Lite => 0
+</%ARGS>
+
+<%INIT>
+
+my @Default = ref($Default) eq 'ARRAY' ? @$Default : ( $Default);
+
+my $Classes=new RT::FM::ClassCollection($session{'CurrentUser'});
+$Classes->LimitToEnabled();
+
+my $d = new RT::FM::Class($session{'CurrentUser'});
+$d->Load($Default[0]);
+
+</%INIT>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/ShowTopic
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Elements/ShowTopic	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,33 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+
+% for (@path) {
+<% $_->ParentObj->Id ? " > " : "" %><a href="<% $RT::WebPath %>/SelfService/RTFM/Topics.html?class=<% $_->ObjectId %>&id=<% $_->Id %>"><% $_->Name || loc("(no name)") %></a>
+% }
+
+<%args>
+$topic
+</%args>
+
+<%init>
+my @path;
+while ($topic->Id) {
+    unshift @path, $topic;
+    $topic = $topic->ParentObj;
+}
+</%init>
\ No newline at end of file

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Topics.html
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/Topics.html	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,236 @@
+%# BEGIN LICENSE BLOCK
+%# 
+%#  Copyright (c) 2002-2003 Jesse Vincent <jesse at bestpractical.com>
+%#  
+%#  This program is free software; you can redistribute it and/or modify
+%#  it under the terms of version 2 of the GNU General Public License 
+%#  as published by the Free Software Foundation.
+%# 
+%#  A copy of that license should have arrived with this
+%#  software, but in any event can be snarfed from www.gnu.org.
+%# 
+%#  This program is distributed in the hope that it will be useful,
+%#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+%#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%#  GNU General Public License for more details.
+%# 
+%# END LICENSE BLOCK
+
+<& /SelfService/Elements/Header, Title => loc('Article Topics') &>
+
+<& /Elements/ListActions, actions => \@Actions &>
+<a href="Topics.html"><&|/l&>All topics</&></a>
+% if (defined $class) {
+&gt; <a href="Topics.html?class=<%$currclass->Id%>"><% $currclass->Name %></a>
+% }
+
+% if ($id != 0) {
+&gt; <& /SelfService/RTFM/Elements/ShowTopic, topic => $currtopic &>
+% }
+<br />
+<h1><&|/l&>Browse by topic</&></h1>
+<%perl>
+if (defined $class) {
+   $m->print('<h2>'.'<a href="'.
+   $RT::WebPath."/SelfService/RTFM/Topics.html?class=" . $currclass->Id
+   .'">'.$currclass->Name."</a></h2>\n");
+   ProduceTree(\@Actions, $currclass, 0, $id);
+} else {
+    $m->print("<ul>\n");
+    while (my $c = $Classes->Next) {
+        $m->print('<li><h2>'.'<a href="'.
+        $RT::WebPath."/SelfService/RTFM/Topics.html?class=" . $c->Id
+        .'">'.$c->Name."</a></h2>\n");
+        $m->print("\n</li>\n");
+    }
+    $m->print("</ul>\n");
+}
+</%perl>
+
+<br />
+<%perl>
+my @articles;
+if ($id or $showall) {
+    my $Articles = RT::FM::ObjectTopicCollection->new($session{'CurrentUser'});
+    $Articles->Limit(FIELD => 'ObjectType', VALUE => 'RT::FM::Article');
+    if ($id) {
+        $Articles->Limit(FIELD => 'Topic', VALUE => $id, ENTRYAGGREGATOR => 'OR');
+        if ($showall) {
+            my $kids = $currtopic->Children;
+            while (my $k = $kids->Next) {
+                $Articles->Limit(FIELD => 'Topic', VALUE => $k->Id,
+                                 ENTRYAGGREGATOR => 'OR');
+            }
+        }
+    }
+    @articles = map {$a = RT::FM::Article->new($session{'CurrentUser'}); $a->Load($_->ObjectId); $a} @{$Articles->ItemsArrayRef}
+} elsif ($class) {
+    my $Articles = RT::FM::ArticleCollection->new($session{'CurrentUser'});
+    my $TopicsAlias = $Articles->Join(
+        TYPE   => 'left',
+        ALIAS1 => 'main',
+        FIELD1 => 'id',
+        TABLE2 => 'FM_ObjectTopics',
+        FIELD2 => 'ObjectId',
+    );
+    $Articles->Limit(
+        LEFTJOIN => $TopicsAlias,
+        FIELD    => 'ObjectType',
+        VALUE    => 'RT::FM::Article',
+    );
+    $Articles->Limit(
+        ALIAS      => $TopicsAlias,
+        FIELD      => 'Topic',
+        OPERATOR   => 'IS',
+        VALUE      => 'NULL',
+        QUOTEVALUE => 0,
+    );
+    $Articles->Limit(
+        FIELD      => 'Class',
+        OPERATOR   => '=',
+        VALUE      => $class,
+    );
+    @articles = @{$Articles->ItemsArrayRef};
+}
+</%perl>
+
+% if (@articles) {
+% if ($id) {
+<h2><&|/l&>Articles in this topic</&></h2>
+% } elsif ($class) {
+<h2><&|/l&>Articles with no topics</&></h2>
+% }
+<ul>
+% for my $a (@articles) {
+<li><a href="Article/Display.html?id=<% $a->Id %>"><% $a->Name || loc("(no name)") %></a></li>
+% }
+</ul>
+% } else {
+%#<i><&|/l&>No articles found in this topic</&></i>
+% }
+
+
+<%init>
+my @Actions;
+my $Classes;
+my $currclass;
+my $currtopic;
+
+if ($class) {
+    $currclass = RT::FM::Class->new($session{'CurrentUser'});
+    $currclass->Load($class);
+} else {
+    $Classes = RT::FM::ClassCollection->new($session{'CurrentUser'});
+    $Classes->LimitToEnabled(); 
+}
+
+if ($id) {
+    $currtopic = RT::FM::Topic->new($session{'CurrentUser'});
+    $currtopic->Load($id);
+}
+
+# A subroutine that iterates through topics and their children, producing
+# the necessary ul, li, and href links for the table of contents.  Thank
+# heaven for query caching.  The $restrict variable is used to display only
+# the branch of the hierarchy which contains that topic ID.
+
+sub ProduceTree {
+    my ($Actions, $c, $parentid, $restrictid) = @_;
+    $parentid = 0 unless $parentid;
+
+    # Deal with tree restriction, if any.
+    if ($restrictid) {
+        my $rtopic = RT::FM::Topic->new($session{'CurrentUser'});
+	$rtopic->Load($restrictid);
+	unless ($rtopic->Id() &&
+	        $rtopic->ObjectId() == $c->Id) {
+	    push(@{$Actions}, "Could not restrict view to topic $restrictid");
+	    # Start over, without the restriction.
+	    &ProduceTree($Actions, $c, $parentid, undef);
+	} else {
+	    my @showtopics;
+	    push(@showtopics, $rtopic);
+	    my $parent = $rtopic->ParentObj;
+	    while ($parent->Id) {
+		push(@showtopics, $parent);
+		my $newparent = $parent->ParentObj;
+		$parent = $newparent;
+	    }
+	    
+	    # List the topics.
+            my $indents = @showtopics;
+	    while (my $t = pop @showtopics) {
+		print "<ul>";
+		print &MakeLinks($t, $c, $t->Children->Count);
+		if ($t->Id == $restrictid) {
+		    &ProduceTree($Actions, $c, $restrictid, undef);
+		}
+	    }
+            print "</ul>" x $indents;
+	}
+    } else {
+	# No restriction in place.  Build the entire tree.
+	my $topics = RT::FM::TopicCollection->new($session{'CurrentUser'});
+	$topics->LimitToObject($c);
+	$topics->LimitToKids($parentid);
+        print "<ul>" if $topics->Count;
+	while (my $t = $topics->Next) {
+	    if ($t->Children->Count) {
+		print &MakeLinks($t, $c, 1);
+		&ProduceTree($Actions, $c, $t->Id);
+	    } else {
+		print &MakeLinks($t, $c, 0);
+	    }
+	}
+        print "</ul>\n" if $topics->Count;
+    }
+}
+
+sub MakeLinks {
+    my ($topic, $c, $haschild) = @_;
+    my $query;
+    my $output;
+
+    if (ref($topic) eq 'RT::FM::Topic') {
+	if ($haschild) {
+	    $query = "Topics.html?id=" . $topic->Id . "&class=" . $c->Id;
+	} else {
+	    $query = "Topics.html?id=" . $topic->Id . "&class=" . $c->Id .
+		"&showall=1";
+	}
+	
+	$output = "<li><a href=\"$query\">" . ($topic->Name() || loc("(no name)"));
+	$output .= ": " . $topic->Description() if $topic->Description();
+	$output .= "</a>";
+	
+        my $Articles = RT::FM::ObjectTopicCollection->new($session{'CurrentUser'});
+        $Articles->Limit(FIELD => 'ObjectType', VALUE => 'RT::FM::Article');
+        $Articles->Limit(FIELD => 'Topic', VALUE => $topic->Id);
+        $output .= " (".loc("[quant,_1,article]", $Articles->Count).")" if $Articles->Count;
+
+	if ($haschild) {
+        #  Commented out by Jesse, since it seems confusing
+        #  
+        #   $output .= " &nbsp;&nbsp;&nbsp;[ <a href=\"$query&showall=1\">Show all articles</a> ]";
+	}
+	
+	$output .= "</li>\n";
+	
+    } else {
+	# This builds a link for the class specified, with no particular
+	# topic.
+	$query = "Topics.html?class=" . $c->Id;
+	$output = "<li><a href=\"$query\">" . $c->Name . "</a>";
+	$output .= ": " . $c->Description if $c->Description;
+    }
+    
+    return $output;
+}
+
+</%init>
+
+<%args>
+$id => 0
+$class => undef
+$showall => undef
+</%args>

Added: rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/autohandler
==============================================================================
--- (empty file)
+++ rtfm/branches/2.3-EXPERIMENTAL/html/SelfService/RTFM/autohandler	Thu Oct 25 16:08:08 2007
@@ -0,0 +1,58 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%#  
+%# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC 
+%#                                          <jesse at bestpractical.com>
+%# 
+%# (Except where explicitly superseded by other copyright notices)
+%# 
+%# 
+%# LICENSE:
+%# 
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%# 
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%# 
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/copyleft/gpl.html.
+%# 
+%# 
+%# CONTRIBUTION SUBMISSION POLICY:
+%# 
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%# 
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%# 
+%# END BPS TAGGED BLOCK }}}
+<%INIT>
+
+if ( $session{'CurrentUser'}->HasRight( Right => 'ShowArticle',
+                   Object => $RT::FM::System )) {
+    $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS);    
+}
+else {
+    RT::Interface::Web::Redirect($RT::WebURL."SelfService/");
+}
+
+</%INIT>


More information about the Rt-commit mailing list