summaryrefslogtreecommitdiff
path: root/rt/t/articles
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2012-04-24 11:35:56 -0700
committerIvan Kohler <ivan@freeside.biz>2012-04-24 11:35:56 -0700
commit6587f6ba7d047ddc1686c080090afe7d53365bd4 (patch)
treeec77342668e8865aca669c9b4736e84e3077b523 /rt/t/articles
parent47153aae5c2fc00316654e7277fccd45f72ff611 (diff)
first pass RT4 merge, RT#13852
Diffstat (limited to 'rt/t/articles')
-rw-r--r--rt/t/articles/article.t230
-rw-r--r--rt/t/articles/articles.t137
-rw-r--r--rt/t/articles/basic-api.t114
-rw-r--r--rt/t/articles/cfsearch.t94
-rw-r--r--rt/t/articles/class.t76
-rw-r--r--rt/t/articles/interface.t221
-rw-r--r--rt/t/articles/queue-specific-class.t218
-rw-r--r--rt/t/articles/search-interface.t113
-rw-r--r--rt/t/articles/upload-customfields.t90
-rw-r--r--rt/t/articles/uri-a.t28
-rw-r--r--rt/t/articles/uri-articles.t31
11 files changed, 1352 insertions, 0 deletions
diff --git a/rt/t/articles/article.t b/rt/t/articles/article.t
new file mode 100644
index 000000000..33becb023
--- /dev/null
+++ b/rt/t/articles/article.t
@@ -0,0 +1,230 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::Test tests => 67;
+
+use_ok 'RT::Articles';
+use_ok 'RT::Classes';
+use_ok 'RT::Class';
+
+my $CLASS = 'ArticleTest-'.$$;
+
+my $user = RT::CurrentUser->new('root');
+
+my $class = RT::Class->new($user);
+
+
+my ($id, $msg) = $class->Create(Name =>$CLASS);
+ok ($id, $msg);
+
+
+
+my $article = RT::Article->new($user);
+ok (UNIVERSAL::isa($article, 'RT::Article'));
+ok (UNIVERSAL::isa($article, 'RT::Record'));
+ok (UNIVERSAL::isa($article, 'RT::Record'));
+ok (UNIVERSAL::isa($article, 'DBIx::SearchBuilder::Record') , "It's a searchbuilder record!");
+
+
+($id, $msg) = $article->Create( Class => $CLASS, Summary => $CLASS);
+ok ($id, $msg);
+$article->Load($id);
+is ($article->Summary, $CLASS, "The summary is set correct");
+my $at = RT::Article->new($RT::SystemUser);
+$at->Load($id);
+is ($at->id , $id);
+is ($at->Summary, $article->Summary);
+
+
+
+
+my $a1 = RT::Article->new($RT::SystemUser);
+ ($id, $msg) = $a1->Create(Class => $class->id, Name => 'ValidateNameTest'.$$);
+ok ($id, $msg);
+
+
+
+my $a2 = RT::Article->new($RT::SystemUser);
+($id, $msg) = $a2->Create(Class => $class->id, Name => 'ValidateNameTest'.$$);
+ok (!$id, $msg);
+
+my $a3 = RT::Article->new($RT::SystemUser);
+($id, $msg) = $a3->Create(Class => $class->id, Name => 'ValidateNameTest2'.$$);
+ok ($id, $msg);
+($id, $msg) =$a3->SetName('ValidateNameTest'.$$);
+
+ok (!$id, $msg);
+
+($id, $msg) =$a3->SetName('ValidateNametest2'.$$);
+
+ok ($id, $msg);
+
+
+
+
+
+my $newart = RT::Article->new($RT::SystemUser);
+$newart->Create(Name => 'DeleteTest'.$$, Class => '1');
+$id = $newart->Id;
+
+ok($id, "New article has an id");
+
+
+ $article = RT::Article->new($RT::SystemUser);
+$article->Load($id);
+ok ($article->Id, "Found the article");
+my $val;
+ ($val, $msg) = $article->Delete();
+ok ($val, "Article Deleted: $msg");
+
+ $a2 = RT::Article->new($RT::SystemUser);
+$a2->Load($id);
+ok (!$a2->Id, "Did not find the article");
+
+# NOT OK
+#$RT::Handle->SimpleQuery("DELETE FROM Links");
+
+my $article_a = RT::Article->new($RT::SystemUser);
+($id, $msg) = $article_a->Create( Class => $CLASS, Summary => "ArticleTestlink1".$$);
+ok($id,$msg);
+
+my $article_b = RT::Article->new($RT::SystemUser);
+($id, $msg) = $article_b->Create( Class => $CLASS, Summary => "ArticleTestlink2".$$);
+ok($id,$msg);
+
+# Create a link between two articles
+($id, $msg) = $article_a->AddLink( Type => 'RefersTo', Target => $article_b->URI);
+ok($id,$msg);
+
+# Make sure that Article Bs "ReferredToBy" links object refers to to this article
+my $refers_to_b = $article_b->ReferredToBy;
+is($refers_to_b->Count, 1, "Found one thing referring to b");
+my $first = $refers_to_b->First;
+ok ($first->isa('RT::Link'), "IT's an RT link - ref ".ref($first) );
+is($first->TargetObj->Id, $article_b->Id, "Its target is B");
+
+ok($refers_to_b->First->BaseObj->isa('RT::Article'), "Yep. its an article");
+
+
+# Make sure that Article A's "RefersTo" links object refers to this article"
+my $referred_To_by_a = $article_a->RefersTo;
+is($referred_To_by_a->Count, 1, "Found one thing referring to b ".$referred_To_by_a->Count. "-".$referred_To_by_a->First->id . " - ".$referred_To_by_a->Last->id);
+ $first = $referred_To_by_a->First;
+ok ($first->isa('RT::Link'), "IT's an RT link - ref ".ref($first) );
+is ($first->TargetObj->Id, $article_b->Id, "Its target is B - " . $first->TargetObj->Id);
+is ($first->BaseObj->Id, $article_a->Id, "Its base is A");
+
+ok($referred_To_by_a->First->BaseObj->isa('RT::Article'), "Yep. its an article");
+
+# Delete the link
+($id, $msg) = $article_a->DeleteLink(Type => 'RefersTo', Target => $article_b->URI);
+ok($id,$msg);
+
+
+# Create an Article A RefersTo Ticket 1 from the Articles side
+use RT::Ticket;
+
+
+my $tick = RT::Ticket->new($RT::SystemUser);
+$tick->Create(Subject => "Article link test ", Queue => 'General');
+$tick->Load($tick->Id);
+ok ($tick->Id, "Found ticket ".$tick->id);
+($id, $msg) = $article_a->AddLink(Type => 'RefersTo', Target => $tick->URI);
+ok($id,$msg);
+
+# Find all tickets whhich refer to Article A
+
+use RT::Tickets;
+use RT::Links;
+
+my $tix = RT::Tickets->new($RT::SystemUser);
+ok ($tix, "Got an RT::Tickets object");
+ok ($tix->LimitReferredToBy($article_a->URI));
+is ($tix->Count, 1, "Found one ticket linked to that article");
+is ($tix->First->Id, $tick->id, "It's even the right one");
+
+
+
+# Find all articles which refer to Ticket 1
+use RT::Articles;
+
+my $articles = RT::Articles->new($RT::SystemUser);
+ok($articles->isa('RT::Articles'), "Created an article collection");
+ok($articles->isa('RT::SearchBuilder'), "Created an article collection");
+ok($articles->isa('DBIx::SearchBuilder'), "Created an article collection");
+ok($tick->URI, "The ticket does still have a URI");
+$articles->LimitRefersTo($tick->URI);
+
+is($articles->Count(), 1);
+is ($articles->First->Id, $article_a->Id);
+is ($articles->First->URI, $article_a->URI);
+
+
+
+# Find all things which refer to ticket 1 using the RT API.
+
+my $tix2 = RT::Links->new($RT::SystemUser);
+ok ($tix2->isa('RT::Links'));
+ok($tix2->LimitRefersTo($tick->URI));
+is ($tix2->Count, 1);
+is ($tix2->First->BaseObj->URI ,$article_a->URI);
+
+
+
+# Delete the link from the RT side.
+my $t2 = RT::Ticket->new($RT::SystemUser);
+$t2->Load($tick->Id);
+($id, $msg)= $t2->DeleteLink( Base => $article_a->URI, Type => 'RefersTo');
+ok ($id, $msg . " - $id - $msg");
+
+# it is actually deleted
+my $tix3 = RT::Links->new($RT::SystemUser);
+$tix3->LimitReferredToBy($tick->URI);
+is ($tix3->Count, 0);
+
+# Recreate the link from teh RT site
+($id, $msg) = $t2->AddLink( Base => $article_a->URI, Type => 'RefersTo');
+ok ($id, $msg);
+
+# Find all tickets whhich refer to Article A
+
+# Find all articles which refer to Ticket 1
+
+
+
+
+my $art = RT::Article->new($RT::SystemUser);
+($id, $msg) = $art->Create (Class => $CLASS);
+ok ($id,$msg);
+
+ok($art->URI);
+ok($art->__Value('URI') eq $art->URI, "The uri in the db is set correctly");
+
+
+
+
+ $art = RT::Article->new($RT::SystemUser);
+($id, $msg) = $art->Create (Class => $CLASS);
+ok ($id,$msg);
+
+ok($art->URIObj);
+ok($art->__Value('URI') eq $art->URIObj->URI, "The uri in the db is set correctly");
+
+
+my $art_id = $art->id;
+$art = RT::Article->new($RT::SystemUser);
+$art->Load($art_id);
+is ($art->Id, $art_id, "Loaded article 1");
+my $s =$art->Summary;
+($val, $msg) = $art->SetSummary("testFoo");
+ok ($val, $msg);
+ok ($art->Summary eq 'testFoo', "The Summary was set to foo");
+my $t = $art->Transactions();
+my $trans = $t->Last;
+ok ($trans->Type eq 'Set', "It's a Set transaction");
+ok ($trans->Field eq 'Summary', "it is about setting the Summary");
+is ($trans->NewValue , 'testFoo', "The new content is 'foo'");
+is ($trans->OldValue,$s, "the old value was preserved");
+
diff --git a/rt/t/articles/articles.t b/rt/t/articles/articles.t
new file mode 100644
index 000000000..c6fe65106
--- /dev/null
+++ b/rt/t/articles/articles.t
@@ -0,0 +1,137 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::Test tests => 29;
+
+use_ok 'RT::Articles';
+use_ok 'RT::Classes';
+use_ok 'RT::Class';
+
+my $class = RT::Class->new($RT::SystemUser);
+my ( $id, $msg ) = $class->Create( Name => 'CollectionTest-' . $$ );
+ok( $id, $msg );
+
+# Add a custom field to our class
+use_ok('RT::CustomField');
+my $cf = RT::CustomField->new($RT::SystemUser);
+isa_ok($cf, 'RT::CustomField');
+
+($id,$msg) = $cf->Create( Name => 'Articles::Sample-'.$$,
+ Description => 'Test text cf',
+ LookupType => RT::Article->CustomFieldLookupType,
+ Type => 'Freeform'
+ );
+
+
+
+ok($id,$msg);
+
+
+($id,$msg) = $cf->AddToObject($class);
+ok ($id,$msg);
+
+
+
+my $art = RT::Article->new($RT::SystemUser);
+( $id, $msg ) = $art->Create(
+ Class => $class->id,
+ Name => 'Collection-1-' . $$,
+ Summary => 'Coll-1-' . $$,
+ 'CustomField-'.$cf->Name => 'Test-'.$$
+);
+
+ok( $id, $msg );
+
+
+
+
+
+
+my $arts = RT::Articles->new($RT::SystemUser);
+$arts->LimitName( VALUE => 'Collection-1-' . $$ . 'fake' );
+is( $arts->Count, 0,
+ "Found no artlcles with names matching something that is not there" );
+
+my $arts2 = RT::Articles->new($RT::SystemUser);
+$arts2->LimitName( VALUE => 'Collection-1-' . $$ );
+is( $arts2->Count, 1, 'Found one with names matching the word "test"' );
+
+$arts = RT::Articles->new($RT::SystemUser);
+$arts->LimitSummary( VALUE => 'Coll-1-' . $$ . 'fake' );
+is( $arts->Count, 0,
+ 'Found no artlcles with summarys matching something that is not there' );
+
+$arts2 = RT::Articles->new($RT::SystemUser);
+$arts2->LimitSummary( VALUE => 'Coll-1-' . $$ );
+is( $arts2->Count, 1, 'Found one with summarys matching the word "Coll-1"' );
+
+my $new_art = RT::Article->new($RT::SystemUser);
+( $id, $msg ) = $new_art->Create(
+ Class => $class->id,
+ Name => 'CFSearchTest1' . $$,
+ 'CustomField-'.$cf->Name => 'testing' . $$
+);
+
+ok( $id, $msg . " Created a second testable article" );
+
+
+$arts = RT::Articles->new($RT::SystemUser);
+$arts->LimitCustomField( OPERATOR => 'LIKE', VALUE => "esting".$$ );
+is( $arts->Count, 1, "Found 1 cf values matching 'esting" . $$ . "' for an unspecified field");
+
+$arts = RT::Articles->new($RT::SystemUser);
+$arts->LimitCustomField( OPERATOR => '=', VALUE => "esting".$$ );
+is( $arts->Count, 0, "Found 0 cf values EXACTLY matching 'esting" . $$ . "' for an unspecified field");
+
+$arts = RT::Articles->new($RT::SystemUser);
+$arts->LimitCustomField( OPERATOR => '=', VALUE => "testing".$$ );
+is( $arts->Count, 1, "Found 0 cf values EXACTLY matching 'testing" . $$ . "' for an unspecified field");
+
+
+$arts = RT::Articles->new($RT::SystemUser);
+$arts->LimitCustomField( OPERATOR => 'LIKE', VALUE => $$ );
+is( $arts->Count, 2, "Found 1 cf values matching '" . $$ . "' for an unspecified field");
+
+
+# Test searching on named custom fields
+$arts = RT::Articles->new($RT::SystemUser);
+$arts->LimitCustomField( OPERATOR => 'LIKE', VALUE => $$, FIELD => $cf->Name );
+is( $arts->Count, 2, "Found 1 Article with cf values matching '".$$."' for CF named " .$cf->Name);
+
+$arts = RT::Articles->new($RT::SystemUser);
+$arts->LimitCustomField( OPERATOR => 'LIKE', VALUE => $$, FIELD => 'NO-SUCH-CF' );
+is( $arts->Count,0, "Found no cf values matching '".$$."' for CF 'NO-SUCH-CF' " );
+
+$arts = RT::Articles->new($RT::SystemUser);
+$arts->Limit(FIELD =>'Class', VALUE => $class->id);
+
+$arts->LimitCustomField(
+ OPERATOR => 'NOT LIKE',
+ VALUE => 'blah',
+ FIELD => $cf->id
+);
+is(
+ $arts->Count ,2,
+ "Found 1 articles with custom field values not matching blah");
+
+$arts = RT::Articles->new($RT::SystemUser);
+$arts->Limit(FIELD =>'Class', VALUE => $class->id);
+$arts->LimitCustomField( OPERATOR => 'NOT LIKE', VALUE => 'est', FIELD => $cf->id );
+is( $arts->Count , 0, "Found 0 cf values not matching 'est' for CF ".$cf->id. " " . join(',', map {$_->id} @{$arts->ItemsArrayRef}));
+$arts = RT::Articles->new($RT::SystemUser);
+$arts->Limit(FIELD =>'Class', VALUE => $class->id);
+$arts->LimitCustomField( OPERATOR => 'NOT LIKE', VALUE => 'BOGUS', FIELD => $cf->id );
+is( $arts->Count , 2, "Found 2 articles not matching 'BOGUS' for CF ".$cf->id);
+
+my $ac = RT::Articles->new($RT::SystemUser);
+ok( $ac->isa('RT::Articles') );
+ok( $ac->isa('DBIx::SearchBuilder') );
+ok( $ac->LimitRefersTo('http://dead.link') );
+is( $ac->Count, 0 );
+
+$ac = RT::Articles->new($RT::SystemUser);
+ok( $ac->LimitReferredToBy('http://dead.link') );
+is( $ac->Count, 0 );
+
diff --git a/rt/t/articles/basic-api.t b/rt/t/articles/basic-api.t
new file mode 100644
index 000000000..f9f9f89f4
--- /dev/null
+++ b/rt/t/articles/basic-api.t
@@ -0,0 +1,114 @@
+#!/usr/bin/perl
+
+use warnings;
+use strict;
+
+use RT::Test tests => 37;
+
+use_ok('RT::Class');
+
+my $class = RT::Class->new($RT::SystemUser);
+isa_ok($class, 'RT::Class');
+isa_ok($class, 'RT::Record');
+isa_ok($class, 'RT::Record');
+
+
+my $name = 'test-'.$$;
+my ($id,$msg) = $class->Create( Name =>$name, Description => 'Test class');
+ok($id,$msg);
+is ($class->Name, $name);
+is ($class->Description, 'Test class');
+
+
+
+# Test custom fields.
+
+can_ok($class, 'CustomFields');
+can_ok($class, 'AddCustomFieldValue');
+can_ok($class, 'DeleteCustomFieldValue');
+can_ok($class, 'FirstCustomFieldValue');
+can_ok($class, 'CustomFieldValues');
+can_ok($class, 'CurrentUserHasRight');
+
+
+# Add a custom field to our class
+my $cf = RT::CustomField->new($RT::SystemUser);
+isa_ok($cf, 'RT::CustomField');
+
+($id,$msg) = $cf->Create( Name => 'Articles::Sample-'.$$,
+ Description => 'Test text cf',
+ LookupType => RT::Article->CustomFieldLookupType,
+ Type => 'Text'
+ );
+
+
+
+ok($id,$msg);
+
+
+($id,$msg) = $cf->AddToObject($class);
+ok ($id,$msg);
+
+
+# Does our class have a custom field?
+
+my $cfs = $class->ArticleCustomFields;
+isa_ok($cfs, 'RT::CustomFields');
+is($cfs->Count, 1, "We only have one custom field");
+my $found_cf = $cfs->First;
+is ($cf->id, $found_cf->id, "it's the right one");
+
+($id,$msg) = $cf->RemoveFromObject($class);
+
+is($class->ArticleCustomFields->Count, 0, "All gone!");
+
+# Put it back. we want to go forward.
+
+($id,$msg) = $cf->AddToObject($class);
+ok ($id,$msg);
+
+
+
+
+use_ok('RT::Article');
+
+my $art = RT::Article->new($RT::SystemUser);
+($id,$msg) =$art->Create(Class => $class->id,
+ Name => 'Sample'.$$,
+ Description => 'A sample article');
+
+ok($id,"Created article ".$id." - " .$msg);
+
+# make sure there is one transaction.
+
+my $txns = $art->Transactions;
+
+is($txns->Count, 1, "One txn");
+my $txn = $txns->First;
+is ($txn->ObjectType, 'RT::Article');
+is ($txn->ObjectId , $id , "It's the right article");
+is ($txn->Type, 'Create', "It's a create!");
+
+
+my $art_cfs = $art->CustomFields();
+is ($art_cfs->Count, 1, "It has a custom field");
+my $values = $art->CustomFieldValues($art_cfs->First);
+is ($values->Count, 0);
+
+$art->AddCustomFieldValue(Field => $cf->id, Value => 'Testing');
+$values = $art->CustomFieldValues($art_cfs->First->id);
+
+# We added the custom field
+is ($values->Count, 1);
+is ($values->First->Content, 'Testing', "We added the CF");
+
+is ($art->Transactions->Count,2, "We added a second transaction for the custom field addition");
+my $txn2 = $art->Transactions->Last;
+is ($txn2->ObjectId, $art->id);
+is ($txn2->id, ($txn->id +1));
+is ($txn2->Type, 'CustomField');
+is($txn2->NewValue, 'Testing');
+ok (!$txn2->OldValue, "It had no old value");
+
+1;
+
diff --git a/rt/t/articles/cfsearch.t b/rt/t/articles/cfsearch.t
new file mode 100644
index 000000000..49420e581
--- /dev/null
+++ b/rt/t/articles/cfsearch.t
@@ -0,0 +1,94 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use RT::Test tests => 11;
+
+my $suffix = '-'. $$;
+
+use_ok 'RT::Class';
+use_ok 'RT::Article';
+use_ok 'RT::CustomField';
+
+my $classname = 'TestClass';
+my $class = RT::Class->new( $RT::SystemUser );
+{
+ $class->Load( $classname );
+ unless ( $class->Id ) {
+ my ($id, $msg) = $class->Create(
+ Name => $classname,
+ Description => 'class for cf tests',
+ );
+ ok $id, "created class '$classname' #$id"
+ or diag "error: $msg";
+ } else {
+ ok 1, "class '$classname' exists";
+ }
+}
+
+# create cf
+my $cfname = 'TestCF'. $suffix;
+my $cf = RT::CustomField->new( $RT::SystemUser );
+{
+ my ($id, $msg) = $cf->Create(
+ Name => $cfname,
+ LookupType => 'RT::Class-RT::Article',
+ Type => 'Select', MaxValues => 1,
+ Description => 'singleselect cf for tests',
+ );
+ ok $id, "created cf '$cfname' #$id"
+ or diag "error: $msg";
+}
+
+# attach cf to class
+{
+ my ($status, $msg) = $cf->AddToObject( $class );
+ ok $status, "attached the cf to the class"
+ or diag "error: $msg";
+}
+
+# create two cf-values
+{
+ my ($status, $msg) = $cf->AddValue( Name => 'Value1' );
+ ok $status, "added a value to the cf" or diag "error: $msg";
+
+ ($status, $msg) = $cf->AddValue( Name => 'Value2' );
+ ok $status, "added a value to the cf" or diag "error: $msg";
+}
+
+my $article1name = 'TestArticle1'.$suffix;
+my $article1 = RT::Article->new($RT::SystemUser);
+$article1->Create( Name => $article1name, Summary => 'Test', Class => $class->Id);
+$article1->AddCustomFieldValue(Field => $cf->Id, Value => 'Value1');
+
+my $article2name = 'TestArticle2'.$suffix;
+my $article2 = RT::Article->new($RT::SystemUser);
+$article2->Create( Name => $article2name, Summary => 'Test', Class => $class->Id);
+$article2->AddCustomFieldValue(Field => $cf->Id, Value => 'Value2');
+
+# search for articles containing 1st value
+{
+ my $articles = RT::Articles->new( $RT::SystemUser );
+ $articles->UnLimit;
+ $articles->Limit( FIELD => "Class", SUBCLAUSE => 'ClassMatch', VALUE => $class->Id);
+ $articles->LimitCustomField( FIELD => $cf->Id, VALUE => 'Value1' );
+ is $articles->Count, 1, 'found correct number of articles';
+}
+
+{
+ my $articles = RT::Articles->new($RT::SystemUser);
+ $articles->UnLimit;
+ $articles->Limit( FIELD => "Class", SUBCLAUSE => 'ClassMatch', VALUE => $class->Id);
+ $articles->LimitCustomField( FIELD => $cf, VALUE => 'Value1' );
+ is $articles->Count, 1, 'found correct number of articles';
+}
+
+{
+ my $articles = RT::Articles->new($RT::SystemUser);
+ $articles->UnLimit( );
+ $articles->Limit( FIELD => "Class", SUBCLAUSE => 'ClassMatch', VALUE => $class->Id);
+ $articles->LimitCustomField( FIELD => $cf->Name, VALUE => 'Value1' );
+ is $articles->Count, 1, 'found correct number of articles';
+}
+
diff --git a/rt/t/articles/class.t b/rt/t/articles/class.t
new file mode 100644
index 000000000..84d6e23be
--- /dev/null
+++ b/rt/t/articles/class.t
@@ -0,0 +1,76 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::Test tests => 24;
+
+use_ok 'RT::Articles';
+use_ok 'RT::Classes';
+use_ok 'RT::Class';
+
+my $root = RT::CurrentUser->new('root');
+ok ($root->Id, "Loaded root");
+my $cl = RT::Class->new($root);
+ok (UNIVERSAL::isa($cl, 'RT::Class'), "the new class is a class");
+
+my ($id, $msg) = $cl->Create(Name => 'Test-'.$$, Description => 'A test class');
+
+ok ($id, $msg);
+
+# no duplicate class names should be allowed
+($id, $msg) = $cl->Create(Name => 'Test-'.$$, Description => 'A test class');
+
+ok (!$id, $msg);
+
+#class name should be required
+
+($id, $msg) = $cl->Create(Name => '', Description => 'A test class');
+
+ok (!$id, $msg);
+
+
+
+$cl->Load('Test-'.$$);
+ok($cl->id, "Loaded the class we want");
+
+
+
+# Create a new user. make sure they can't create a class
+
+my $u= RT::User->new(RT->SystemUser);
+$u->Create(Name => "ArticlesTest".time, Privileged => 1);
+ok ($u->Id, "Created a new user");
+
+# Make sure you can't create a group with no acls
+$cl = RT::Class->new($u);
+ok (UNIVERSAL::isa($cl, 'RT::Class'), "the new class is a class");
+
+($id, $msg) = $cl->Create(Name => 'Test-nobody'.$$, Description => 'A test class');
+
+
+ok (!$id, $msg. "- Can not create classes as a random new user - " .$u->Id);
+$u->PrincipalObj->GrantRight(Right =>'AdminClass', Object => RT->System);
+($id, $msg) = $cl->Create(Name => 'Test-nobody-'.$$, Description => 'A test class');
+
+ok ($id, $msg. "- Can create classes as a random new user after ACL grant");
+
+# now check the Web UI
+
+my ($url, $m) = RT::Test->started_ok;
+ok($m->login);
+$m->get_ok("$url/Admin/Articles/Classes/Modify.html?Create=1");
+$m->content_contains('Create a Class', 'found title');
+$m->submit_form_ok({
+ form_number => 3,
+ fields => { Name => 'Test Redirect' },
+});
+$m->content_contains('Object created', 'found results');
+$m->content_contains('Modify the Class Test Redirect', 'found title');
+$m->form_number(3);
+$m->untick( 'Include-Name', 1 );
+$m->field( 'Description', 'Test Description' );
+$m->submit();
+$m->content_like(qr/Description changed from.*no value.*to .*Test Description/,'description changed');
+$m->form_number(3);
+is($m->current_form->find_input('Include-Name')->value,undef,'Disabled Including Names for this Class');
diff --git a/rt/t/articles/interface.t b/rt/t/articles/interface.t
new file mode 100644
index 000000000..d04be8b88
--- /dev/null
+++ b/rt/t/articles/interface.t
@@ -0,0 +1,221 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::Test tests => 53;
+
+use RT::CustomField;
+use RT::EmailParser;
+use RT::Queue;
+use RT::Ticket;
+use_ok 'RT::Class';
+use_ok 'RT::Topic';
+use_ok 'RT::Article';
+
+my ($url, $m) = RT::Test->started_ok;
+
+# Variables to test return values
+my ($ret, $msg);
+
+# Create a test class
+my $class = RT::Class->new($RT::SystemUser);
+($ret, $msg) = $class->Create('Name' => 'tlaTestClass-'.$$,
+ 'Description' => 'A general-purpose test class');
+ok($ret, "Test class created");
+my $class2 = RT::Class->new($RT::SystemUser);
+($ret, $msg) = $class2->Create('Name' => 'tlaTestClass2-'.$$,
+ 'Description' => 'Another general-purpose test class');
+ok($ret, "Test class 2 created");
+
+
+# Create a hierarchy of test topics
+my $topic1 = RT::Topic->new($RT::SystemUser);
+my $topic11 = RT::Topic->new($RT::SystemUser);
+my $topic12 = RT::Topic->new($RT::SystemUser);
+my $topic2 = RT::Topic->new($RT::SystemUser);
+my $topic_class2= RT::Topic->new($RT::SystemUser);
+my $gtopic = RT::Topic->new($RT::SystemUser);
+($ret, $msg) = $topic1->Create('Parent' => 0,
+ 'Name' => 'tlaTestTopic1-'.$$,
+ 'ObjectType' => 'RT::Class',
+ 'ObjectId' => $class->Id);
+ok($ret, "Topic 1 created");
+($ret, $msg) = $topic11->Create('Parent' => $topic1->Id,
+ 'Name' => 'tlaTestTopic1.1-'.$$,
+ 'ObjectType' => 'RT::Class',
+ 'ObjectId' => $class->Id);
+ok($ret, "Topic 1.1 created");
+($ret, $msg) = $topic12->Create('Parent' => $topic1->Id,
+ 'Name' => 'tlaTestTopic1.2-'.$$,
+ 'ObjectType' => 'RT::Class',
+ 'ObjectId' => $class->Id);
+ok($ret, "Topic 1.2 created");
+($ret, $msg) = $topic2->Create('Parent' => 0,
+ 'Name' => 'tlaTestTopic2-'.$$,
+ 'ObjectType' => 'RT::Class',
+ 'ObjectId' => $class->Id);
+ok($ret, "Topic 2 created");
+($ret, $msg) = $topic_class2->Create('Parent' => 0,
+ 'Name' => 'tlaTestTopicClass2-'.$$,
+ 'ObjectType' => 'RT::Class',
+ 'ObjectId' => $class2->Id);
+ok($ret, "Topic Class2 created");
+($ret, $msg) = $gtopic->Create('Parent' => 0,
+ 'Name' => 'tlaTestTopicGlobal-'.$$,
+ 'ObjectType' => 'RT::System',
+ 'ObjectId' => $RT::System->Id );
+ok($ret, "Global Topic created");
+
+# Create some article custom fields
+
+my $questionCF = RT::CustomField->new($RT::SystemUser);
+my $answerCF = RT::CustomField->new($RT::SystemUser);
+($ret, $msg) = $questionCF->Create('Name' => 'Question-'.$$,
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Class-RT::Article',
+ 'Description' => 'The question to be answered',
+ 'Disabled' => 0);
+ok($ret, "Question CF created: $msg");
+($ret, $msg) = $answerCF->Create('Name' => 'Answer-'.$$,
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Class-RT::Article',
+ 'Description' => 'The answer to the question',
+ 'Disabled' => 0);
+ok($ret, "Answer CF created: $msg");
+
+# Attach the custom fields to our class
+($ret, $msg) = $questionCF->AddToObject($class);
+ok($ret, "Question CF added to class: $msg");
+($ret, $msg) = $answerCF->AddToObject($class);
+ok($ret, "Answer CF added to class: $msg");
+my ($qid, $aid) = ($questionCF->Id, $answerCF->Id);
+
+my %cvals = ('article1q' => 'Some question about swallows',
+ 'article1a' => 'Some answer about Europe and Africa',
+ 'article2q' => 'Another question about Monty Python',
+ 'article2a' => 'Romani ite domum',
+ 'article3q' => 'Why should I eat my supper?',
+ 'article3a' => 'There are starving children in Africa',
+ 'article4q' => 'What did Brian originally write?',
+ 'article4a' => 'Romanes eunt domus');
+
+# Create an article or two with our custom field values.
+
+my $article1 = RT::Article->new($RT::SystemUser);
+my $article2 = RT::Article->new($RT::SystemUser);
+my $article3 = RT::Article->new($RT::SystemUser);
+my $article4 = RT::Article->new($RT::SystemUser);
+($ret, $msg) = $article1->Create(Name => 'First article '.$$,
+ Summary => 'blah blah 1',
+ Class => $class->Id,
+ Topics => [$topic1->Id],
+ "CustomField-$qid" => $cvals{'article1q'},
+ "CustomField-$aid" => $cvals{'article1a'},
+ );
+ok($ret, "article 1 created");
+($ret, $msg) = $article2->Create(Name => 'Second article '.$$,
+ Summary => 'foo bar 2',
+ Class => $class->Id,
+ Topics => [$topic11->Id],
+ "CustomField-$qid" => $cvals{'article2q'},
+ "CustomField-$aid" => $cvals{'article2a'},
+ );
+ok($ret, "article 2 created");
+($ret, $msg) = $article3->Create(Name => 'Third article '.$$,
+ Summary => 'ping pong 3',
+ Class => $class->Id,
+ Topics => [$topic12->Id],
+ "CustomField-$qid" => $cvals{'article3q'},
+ "CustomField-$aid" => $cvals{'article3a'},
+ );
+ok($ret, "article 3 created");
+($ret, $msg) = $article4->Create(Name => 'Fourth article '.$$,
+ Summary => 'hoi polloi 4',
+ Class => $class->Id,
+ Topics => [$topic2->Id],
+ "CustomField-$qid" => $cvals{'article4q'},
+ "CustomField-$aid" => $cvals{'article4a'},
+ );
+ok($ret, "article 4 created");
+
+# Create a ticket.
+my $parser = RT::EmailParser->new();
+$parser->ParseMIMEEntityFromScalar('From: root@localhost
+To: rt@example.com
+Subject: test ticket for articles
+
+This is some form of new request.
+May as well say something about Africa.');
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my $obj;
+($ret, $obj, $msg) = $ticket->Create(Queue => 'General',
+ Subject => 'test ticket for articles '.$$,
+ MIMEObj => $parser->Entity);
+ok($ret, "Test ticket for articles created: $msg");
+
+
+#### Right. That's our data. Now begin the real testing.
+
+isa_ok($m, 'Test::WWW::Mechanize');
+ok($m->login, 'logged in');
+$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! },
+ 'UI -> Articles' );
+
+$m->content_contains($article3->Name);
+$m->follow_link_ok( {text => $article3->Name}, 'Articles -> '. $article3->Name );
+$m->title_is("Article #" . $article3->Id . ": " . $article3->Name);
+$m->follow_link_ok( { text => 'Modify'}, 'Article -> Modify' );
+
+{
+$m->content_like(qr/Refers to/, "found links edit box");
+my $ticket_id = $ticket->Id;
+my $turi = "t:$ticket_id";
+my $a1uri = 'a:'.$article1->Id;
+$m->submit_form(form_name => 'EditArticle',
+ fields => { $article3->Id.'-RefersTo' => $turi,
+ 'RefersTo-'.$article3->Id => $a1uri }
+ );
+
+$m->content_like(qr/Ticket.*$ticket_id/, "Ticket linkto was created");
+$m->content_like(qr/URI.*$a1uri/, "Article linkfrom was created");
+}
+
+# Now try to extract an article from a link.
+$m->get_ok($url."/Ticket/Display.html?id=".$ticket->Id,
+ "Loaded ticket display");
+$m->content_like(qr/Extract Article/, "Article extraction link shows up");
+$m->follow_link_ok( { text => 'Extract Article' }, '-> Extract Article' );
+$m->content_contains($class->Name);
+$m->follow_link_ok( { text => $class->Name }, 'Extract Article -> '. $class->Name );
+$m->content_like(qr/Select topics for this article/i, 'selecting topic');
+$m->form_number(3);
+$m->set_visible([option => $topic1->Name]);
+$m->submit;
+$m->form_number(3);
+$m->set_visible([option => $answerCF->Name]);
+$m->click();
+$m->title_like(qr/Create a new article/, "got edit page from extraction");
+$m->submit_form(form_name => 'EditArticle');
+$m->title_like(qr/Modify article/);
+$m->follow_link_ok( { text => 'Display' }, '-> Display' );
+$m->content_like(qr/Africa/, "Article content exist");
+$m->content_contains($ticket->Subject,
+ "Article references originating ticket");
+
+diag("Test creating a ticket in Class2 and make sure we don't see Class1 Topics") if $ENV{TEST_VERBOSE};
+{
+$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! },
+ 'UI -> Articles' );
+$m->follow_link_ok( {text => 'New Article' }, 'Articles -> New Article' );
+$m->follow_link_ok( {text => 'in class '.$class2->Name }, 'New Article -> in class '.$class2->Name );
+$m->content_lacks( $topic1->Name, "Topic1 from Class1 isn't shown" );
+$m->content_lacks( $topic11->Name, "Topic11 from Class1 isn't shown" );
+$m->content_lacks( $topic12->Name, "Topic12 from Class1 isn't shown" );
+$m->content_lacks( $topic2->Name, "Topic2 from Class1 isn't shown" );
+$m->content_contains( $gtopic->Name, "Global Topic is shown" );
+$m->content_contains( $topic_class2->Name, "Class2 topic is shown" );
+}
diff --git a/rt/t/articles/queue-specific-class.t b/rt/t/articles/queue-specific-class.t
new file mode 100644
index 000000000..a73d583c3
--- /dev/null
+++ b/rt/t/articles/queue-specific-class.t
@@ -0,0 +1,218 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::Test tests => 56;
+
+my ( $url, $m ) = RT::Test->started_ok;
+$m->login;
+
+my %class = map { $_ => '' } qw/foo bar/;
+
+diag "create classes foo and bar" if $ENV{TEST_VERBOSE};
+
+for my $name ( keys %class ) {
+ $m->get_ok( '/Admin/Articles/Classes/Modify.html?Create=1',
+ 'class create page' );
+
+ $m->submit_form(
+ form_number => 3,
+ fields => { Name => $name, HotList => 1 },
+ );
+
+ $m->content_contains( "Modify the Class $name",
+ 'created class $name' );
+ my ($id) = ( $m->content =~ /name="id" value="(\d+)"/ );
+ ok( $id, "id of $name" );
+ $class{$name} = $id;
+}
+
+diag "create articles in foo and bar" if $ENV{TEST_VERBOSE};
+
+for my $name ( keys %class ) {
+ $m->get_ok( '/Articles/Article/Edit.html?Class=' . $class{$name},
+ 'article create page' );
+
+ $m->submit_form(
+ form_number => 3,
+ fields => { Name => "article $name" }
+ );
+
+ $m->content_like( qr/Article \d+ created/, "created article $name" );
+}
+
+diag "apply foo to queue General" if $ENV{TEST_VERBOSE};
+{
+ $m->get_ok( '/Admin/Articles/Classes/Objects.html?id=' . $class{foo},
+ 'apply page' );
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'AddClass-' . $class{foo} => 1 },
+ button => 'UpdateObjs',
+ );
+ $m->content_contains( 'Object created', 'applied foo to General' );
+}
+
+my $ticket_id;
+
+diag "create ticket in General" if $ENV{TEST_VERBOSE};
+
+{
+ $m->get_ok( '/Ticket/Create.html?Queue=1', 'ticket create page' );
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'Subject' => 'test article', Content => 'test article' },
+ );
+ ($ticket_id) = ( $m->content =~ /Ticket \d+ created/ );
+ ok( $ticket_id, "id of ticket: $ticket_id" );
+}
+
+diag "update ticket to see if there is article foo"
+ if $ENV{TEST_VERBOSE};
+
+{
+ $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id,
+ 'ticket update page' );
+ $m->content_contains( 'article foo:', 'got article foo in hotlist' );
+ $m->content_lacks( 'article bar:', 'no article bar in hotlist' );
+
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'Articles_Content' => 'article' },
+ button => 'Go',
+ );
+ $m->content_like( qr/article foo.*article foo/s, 'selected article foo' );
+ $m->content_lacks( 'article bar', 'no article bar' );
+
+ $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id,
+ 'ticket update page' );
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'Articles-Include-Article-Named' => 'article foo' },
+ button => 'Go',
+ );
+ $m->content_like( qr/article foo.*article foo/s, 'selected article foo' );
+ $m->content_lacks( 'article bar', 'no article bar' );
+
+ $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id,
+ 'ticket update page' );
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'Articles-Include-Article-Named' => 'articlei bar' },
+ button => 'Go',
+ );
+ $m->content_unlike( qr/article foo.*article foo/s, 'no article foo' );
+ $m->content_lacks( 'article bar', 'no article bar' );
+}
+
+diag "apply bar to globally" if $ENV{TEST_VERBOSE};
+{
+ $m->get_ok( '/Admin/Articles/Classes/Objects.html?id=' . $class{bar},
+ 'apply page' );
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'AddClass-' . $class{bar} => 0 },
+ button => 'UpdateObjs',
+ );
+ $m->content_contains( 'Object created', 'applied bar globally' );
+}
+
+diag "update ticket to see if there are both article foo and bar"
+ if $ENV{TEST_VERBOSE};
+
+{
+ $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id,
+ 'ticket update page' );
+ $m->content_contains( 'article foo:', 'got article foo in hotlist' );
+ $m->content_contains( 'article bar:', 'got article bar in hotlist' );
+
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'Articles_Content' => 'article' },
+ button => 'Go',
+ );
+ $m->content_like( qr/article foo.*article foo/s, 'selected article foo' );
+ $m->content_like( qr/article bar.*article bar/s, 'selected article bar' );
+
+ $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id,
+ 'ticket update page' );
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'Articles-Include-Article-Named' => 'article foo' },
+ button => 'Go',
+ );
+ $m->content_like( qr/article foo.*article foo/s, 'selected article foo' );
+ $m->content_unlike( qr/article bar.*article bar/s, 'no article bar' );
+
+ $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id,
+ 'ticket update page' );
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'Articles-Include-Article-Named' => 'article bar' },
+ button => 'Go',
+ );
+ $m->content_like( qr/article bar.*article bar/s, 'selected article bar' );
+ $m->content_unlike( qr/article foo.*article foo/s, 'no article foo' );
+}
+
+
+diag "remove both foo and bar" if $ENV{TEST_VERBOSE};
+{
+ $m->get_ok( '/Admin/Articles/Classes/Objects.html?id=' . $class{foo},
+ 'apply page' );
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'RemoveClass-' . $class{foo} => 1 },
+ button => 'UpdateObjs',
+ );
+ $m->content_contains( 'Object deleted', 'removed foo' );
+
+ $m->get_ok( '/Admin/Articles/Classes/Objects.html?id=' . $class{bar},
+ 'apply page' );
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'RemoveClass-' . $class{bar} => 0 },
+ button => 'UpdateObjs',
+ );
+ $m->content_contains( 'Object deleted', 'remoked bar' );
+}
+
+diag "update ticket to see if there are both article foo and bar"
+ if $ENV{TEST_VERBOSE};
+
+{
+ $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id,
+ 'ticket update page' );
+ $m->content_lacks( 'article foo:', 'no article foo in hotlist' );
+ $m->content_lacks( 'article bar:', 'no article bar in hotlist' );
+
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'Articles_Content' => 'article' },
+ button => 'Go',
+ );
+ $m->content_lacks( 'article foo', 'no article foo' );
+ $m->content_lacks( 'article bar', 'no article bar' );
+
+ $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id,
+ 'ticket update page' );
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'Articles-Include-Article-Named' => 'article foo' },
+ button => 'Go',
+ );
+ $m->content_lacks( 'article foo', 'no article foo' );
+ $m->content_lacks( 'article bar', 'no article bar' );
+
+ $m->get_ok( '/Ticket/Update.html?Action=Comment&id=' . $ticket_id,
+ 'ticket update page' );
+ $m->submit_form(
+ form_number => 3,
+ fields => { 'Articles-Include-Article-Named' => 'article bar' },
+ button => 'Go',
+ );
+ $m->content_lacks( 'article foo', 'no article foo' );
+ $m->content_lacks( 'article bar', 'no article bar' );
+}
+
diff --git a/rt/t/articles/search-interface.t b/rt/t/articles/search-interface.t
new file mode 100644
index 000000000..eb3a4f763
--- /dev/null
+++ b/rt/t/articles/search-interface.t
@@ -0,0 +1,113 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::Test tests => 23;
+
+use RT::CustomField;
+use RT::Queue;
+use RT::Ticket;
+use_ok 'RT::Class';
+use_ok 'RT::Topic';
+use_ok 'RT::Article';
+
+my ($url, $m) = RT::Test->started_ok;
+
+# Variables to test return values
+my ($ret, $msg);
+
+# Create a test class
+my $class = RT::Class->new($RT::SystemUser);
+($ret, $msg) = $class->Create('Name' => 'tlaTestClass-'.$$,
+ 'Description' => 'A general-purpose test class');
+ok($ret, "Test class created");
+
+
+my $questionCF = RT::CustomField->new($RT::SystemUser);
+my $answerCF = RT::CustomField->new($RT::SystemUser);
+my $ticketCF = RT::CustomField->new($RT::SystemUser);
+($ret, $msg) = $questionCF->Create('Name' => 'Question-'.$$,
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Class-RT::Article',
+ 'Description' => 'The question to be answered',
+ 'Disabled' => 0);
+ok($ret, "Question CF created: $msg");
+($ret, $msg) = $answerCF->Create('Name' => 'Answer-'.$$,
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Class-RT::Article',
+ 'Description' => 'The answer to the question',
+ 'Disabled' => 0);
+ok($ret, "Answer CF created: $msg");
+
+($ret, $msg) = $ticketCF->Create('Name' => 'Class',
+ 'Type' => 'Text',
+ 'MaxValues' => 1,
+ 'LookupType' => 'RT::Queue-RT::Ticket',
+ 'Disabled' => 0);
+ok($ret, "Ticket CF 'Class' created: $msg");
+
+# Attach the custom fields to our class
+($ret, $msg) = $questionCF->AddToObject($class);
+ok($ret, "Question CF added to class: $msg");
+($ret, $msg) = $answerCF->AddToObject($class);
+ok($ret, "Answer CF added to class: $msg");
+my ($qid, $aid) = ($questionCF->Id, $answerCF->Id);
+
+my $global_queue = RT::Queue->new($RT::SystemUser);
+($ret, $msg) = $ticketCF->AddToObject($global_queue);
+ok($ret, "Ticket CF added globally: $msg");
+
+my %cvals = ('article1q' => 'Some question about swallows',
+ 'article1a' => 'Some answer about Europe and Africa',
+ 'article2q' => 'Another question about Monty Python',
+ 'article2a' => 'Romani ite domum',
+ 'article3q' => 'Why should I eat my supper?',
+ 'article3a' => 'There are starving children in Africa',
+ 'article4q' => 'What did Brian originally write?',
+ 'article4a' => 'Romanes eunt domus');
+
+# Create an article or two with our custom field values.
+
+my $article1 = RT::Article->new($RT::SystemUser);
+my $article2 = RT::Article->new($RT::SystemUser);
+my $article3 = RT::Article->new($RT::SystemUser);
+my $article4 = RT::Article->new($RT::SystemUser);
+($ret, $msg) = $article1->Create(Name => 'First article '.$$,
+ Summary => 'blah blah 1',
+ Class => $class->Id,
+ "CustomField-$qid" => $cvals{'article1q'},
+ "CustomField-$aid" => $cvals{'article1a'},
+ );
+ok($ret, "article 1 created");
+($ret, $msg) = $article2->Create(Name => 'Second article '.$$,
+ Summary => 'foo bar 2',
+ Class => $class->Id,
+ "CustomField-$qid" => $cvals{'article2q'},
+ "CustomField-$aid" => $cvals{'article2a'},
+ );
+ok($ret, "article 2 created");
+($ret, $msg) = $article3->Create(Name => 'Third article '.$$,
+ Summary => 'ping pong 3',
+ Class => $class->Id,
+ "CustomField-$qid" => $cvals{'article3q'},
+ "CustomField-$aid" => $cvals{'article3a'},
+ );
+ok($ret, "article 3 created");
+($ret, $msg) = $article4->Create(Name => 'Fourth article '.$$,
+ Summary => 'hoi polloi 4',
+ Class => $class->Id,
+ "CustomField-$qid" => $cvals{'article4q'},
+ "CustomField-$aid" => $cvals{'article4a'},
+ );
+ok($ret, "article 4 created");
+
+isa_ok($m, 'Test::WWW::Mechanize');
+ok($m->login, 'logged in');
+$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! },
+ 'UI -> Articles' );
+$m->follow_link_ok( {text => 'Search'}, 'Articles -> Search');
+$m->follow_link_ok( {text => 'in class '.$class->Name}, 'Articles in class '.$class->Name);
+$m->content_contains($article1->Name);
diff --git a/rt/t/articles/upload-customfields.t b/rt/t/articles/upload-customfields.t
new file mode 100644
index 000000000..912c23ded
--- /dev/null
+++ b/rt/t/articles/upload-customfields.t
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::Test tests => 20;
+
+use RT;
+my $logo;
+BEGIN {
+ $logo =
+ -e $RT::MasonComponentRoot . '/NoAuth/images/bpslogo.png'
+ ? 'bpslogo.png'
+ : 'bplogo.gif';
+}
+
+use constant ImageFile => $RT::MasonComponentRoot . "/NoAuth/images/$logo";
+
+use constant ImageFileContent => do {
+ local $/;
+ open my $fh, '<', ImageFile or die ImageFile.$!;
+ binmode($fh);
+ scalar <$fh>;
+};
+
+use RT::Class;
+my $class = RT::Class->new($RT::SystemUser);
+my ($ret, $msg) = $class->Create('Name' => 'tlaTestClass-'.$$,
+ 'Description' => 'A general-purpose test class');
+ok($ret, "Test class created");
+
+
+my ($url, $m) = RT::Test->started_ok;
+isa_ok($m, 'Test::WWW::Mechanize');
+ok($m->login, 'logged in');
+$m->follow_link_ok( { text => 'Configuration' } );
+$m->title_is(q/RT Administration/, 'admin screen');
+$m->follow_link_ok( { text => 'Custom Fields' } );
+$m->title_is(q/Select a Custom Field/, 'admin-cf screen');
+$m->follow_link_ok( { text => 'Create', url_regex => qr{^/Admin/CustomFields/} } );
+$m->submit_form(
+ form_name => "ModifyCustomField",
+ fields => {
+ TypeComposite => 'Image-0',
+ LookupType => 'RT::Class-RT::Article',
+ Name => 'img'.$$,
+ Description => 'img',
+ },
+);
+$m->title_is(qq/Editing CustomField img$$/, 'admin-cf created');
+$m->follow_link( text => 'Applies to' );
+$m->title_is(qq/Modify associated objects for img$$/, 'pick cf screenadmin screen');
+$m->form_number(3);
+
+my $tcf = (grep { /AddCustomField-/ } map { $_->name } $m->current_form->inputs )[0];
+$m->tick( $tcf, 0 ); # Associate the new CF with this queue
+$m->click('UpdateObjs');
+$m->content_like( qr/Object created/, 'TCF added to the queue' );
+
+$m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! },
+ 'UI -> Articles' );
+$m->follow_link( text => 'New Article');
+
+$m->follow_link( url_regex => qr/Edit.html\?Class=1/ );
+$m->title_is(qq/Create a new article/);
+
+$m->content_like(qr/Upload multiple images/, 'has a upload image field');
+
+$tcf =~ /(\d+)$/ or die "Hey this is impossible dude";
+my $upload_field = "Object-RT::Article--CustomField-$1-Upload";
+
+diag("Uploading an image to $upload_field") if $ENV{TEST_VERBOSE};
+
+$m->submit_form(
+ form_name => "EditArticle",
+ fields => {
+ $upload_field => ImageFile,
+ Name => 'Image Test '.$$,
+ Summary => 'testing img cf creation',
+ },
+);
+
+$m->content_like(qr/Article \d+ created/, "an article was created succesfully");
+
+my $id = $1 if $m->content =~ /Article (\d+) created/;
+
+$m->title_like(qr/Modify article #$id/, "Editing article $id");
+
+$m->follow_link( text => $logo );
+$m->content_is(ImageFileContent, "it links to the uploaded image");
diff --git a/rt/t/articles/uri-a.t b/rt/t/articles/uri-a.t
new file mode 100644
index 000000000..82d0f1b01
--- /dev/null
+++ b/rt/t/articles/uri-a.t
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::Test tests => 7;
+
+use_ok("RT::URI::a");
+my $uri = RT::URI::a->new($RT::SystemUser);
+ok(ref($uri), "URI object exists");
+
+my $class = RT::Class->new( $RT::SystemUser );
+$class->Create( Name => 'URItest - '. $$ );
+ok $class->id, 'created a class';
+my $article = RT::Article->new( $RT::SystemUser );
+my ($id, $msg) = $article->Create(
+ Name => 'Testing URI parsing - '. $$,
+ Summary => 'In which this should load',
+ Class => $class->Id
+);
+ok($id,$msg);
+
+my $uristr = "a:" . $article->Id;
+$uri->ParseURI($uristr);
+is(ref($uri->Object), "RT::Article", "Object loaded is an article");
+is($uri->Object->Id, $article->Id, "Object loaded has correct ID");
+is($article->URI, 'fsck.com-article://example.com/article/'.$article->Id,
+ "URI object has correct URI string");
diff --git a/rt/t/articles/uri-articles.t b/rt/t/articles/uri-articles.t
new file mode 100644
index 000000000..4124b1942
--- /dev/null
+++ b/rt/t/articles/uri-articles.t
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::Test tests => 9;
+
+use_ok "RT::URI::fsck_com_article";
+my $uri = RT::URI::fsck_com_article->new( $RT::SystemUser );
+
+ok $uri;
+isa_ok $uri, 'RT::URI::fsck_com_article';
+isa_ok $uri, 'RT::URI::base';
+isa_ok $uri, 'RT::Base';
+
+is $uri->LocalURIPrefix, 'fsck.com-article://example.com/article/';
+
+my $class = RT::Class->new( $RT::SystemUser );
+$class->Create( Name => 'URItest - '. $$ );
+ok $class->id, 'created a class';
+my $article = RT::Article->new( $RT::SystemUser );
+my ($id, $msg) = $article->Create(
+ Name => 'Testing URI parsing - '. $$,
+ Summary => 'In which this should load',
+ Class => $class->Id
+);
+ok($id,$msg);
+
+$uri = RT::URI::fsck_com_article->new( $article->CurrentUser );
+is $uri->LocalURIPrefix . $article->id, $uri->URIForObject( $article );
+