[Rt-commit] [svn] r1212 - in rtfm/trunk: etc
html/Callbacks/RTFM/autohandler html/RTFM
html/RTFM/Admin/Classes html/RTFM/Admin/Elements
html/RTFM/Article html/RTFM/Article/Elements
html/RTFM/Elements lib/RT/FM
alexmv at pallas.eruditorum.org
alexmv at pallas.eruditorum.org
Mon Jul 12 18:31:21 EDT 2004
Author: alexmv
Date: Mon Jul 12 18:31:21 2004
New Revision: 1212
Added:
rtfm/trunk/html/RTFM/Admin/Classes/Topics.html
rtfm/trunk/html/RTFM/Article/Elements/EditTopics
rtfm/trunk/html/RTFM/Article/Elements/ShowTopics
rtfm/trunk/html/RTFM/Topics.html
rtfm/trunk/lib/RT/FM/ObjectTopic.pm
rtfm/trunk/lib/RT/FM/ObjectTopicCollection.pm
rtfm/trunk/lib/RT/FM/ObjectTopicCollection_Overlay.pm
rtfm/trunk/lib/RT/FM/Topic.pm
rtfm/trunk/lib/RT/FM/TopicCollection.pm
rtfm/trunk/lib/RT/FM/TopicCollection_Overlay.pm
rtfm/trunk/lib/RT/FM/Topic_Overlay.pm
Modified:
rtfm/trunk/etc/acl.Oracle
rtfm/trunk/etc/acl.Pg
rtfm/trunk/etc/drop_schema.Oracle
rtfm/trunk/etc/drop_schema.Pg
rtfm/trunk/etc/drop_schema.mysql
rtfm/trunk/etc/schema.Oracle
rtfm/trunk/etc/schema.Pg
rtfm/trunk/etc/schema.mysql
rtfm/trunk/html/Callbacks/RTFM/autohandler/Default
rtfm/trunk/html/RTFM/Admin/Elements/ClassTabs
rtfm/trunk/html/RTFM/Article/Display.html
rtfm/trunk/html/RTFM/Article/Edit.html
rtfm/trunk/html/RTFM/Article/PreCreate.html
rtfm/trunk/html/RTFM/Elements/Tabs
rtfm/trunk/lib/RT/FM/ArticleCollection_Overlay.pm
rtfm/trunk/lib/RT/FM/Article_Overlay.pm
rtfm/trunk/lib/RT/FM/Class_Overlay.pm
Log:
* First pass at topic hierarchies
Modified: rtfm/trunk/etc/acl.Oracle
==============================================================================
--- rtfm/trunk/etc/acl.Oracle (original)
+++ rtfm/trunk/etc/acl.Oracle Mon Jul 12 18:31:21 2004
@@ -1,4 +1,4 @@
-# mysql doesn't need new acls
+# Oracle doesn't need new acls
sub acl {
return ('select id from Users where id = 1');
}
Modified: rtfm/trunk/etc/acl.Pg
==============================================================================
--- rtfm/trunk/etc/acl.Pg (original)
+++ rtfm/trunk/etc/acl.Pg Mon Jul 12 18:31:21 2004
@@ -5,7 +5,7 @@
my @acls;
my @tables = qw (
-fm_classes_id_seq
+FM_Classes_id_seq
FM_Classes
FM_ClassCustomFields_id_seq
FM_ClassCustomFields
@@ -19,6 +19,10 @@
FM_ArticleCFValues
FM_Transactions_id_seq
FM_Transactions
+FM_Topics_id_seq
+FM_Topics
+FM_ObjectTopics_id_seq
+FM_ObjectTopics
);
Modified: rtfm/trunk/etc/drop_schema.Oracle
==============================================================================
--- rtfm/trunk/etc/drop_schema.Oracle (original)
+++ rtfm/trunk/etc/drop_schema.Oracle Mon Jul 12 18:31:21 2004
@@ -5,7 +5,8 @@
DROP TABLE FM_CustomFieldValues ;
DROP TABLE FM_ArticleCFValues ;
DROP TABLE FM_Transactions ;
-DROP TABLE FM_Deltas ;
+DROP TABLE FM_ObjectTopics ;
+DROP TABLE FM_Topics ;
DROP sequence FM_Classes_seq;
DROP sequence FM_ClassCustomFields_seq;
DROP sequence FM_CustomFields_seq;
@@ -13,4 +14,5 @@
DROP sequence FM_CustomFieldValues_seq;
DROP sequence FM_ArticleCFValues_seq;
DROP sequence FM_Transactions_seq;
-DROP sequence FM_Deltas_seq;
+DROP sequence FM_ObjectTopics_seq;
+DROP sequence FM_Topics_seq;
Modified: rtfm/trunk/etc/drop_schema.Pg
==============================================================================
--- rtfm/trunk/etc/drop_schema.Pg (original)
+++ rtfm/trunk/etc/drop_schema.Pg Mon Jul 12 18:31:21 2004
@@ -5,7 +5,8 @@
DROP TABLE FM_CustomFieldValues ;
DROP TABLE FM_ArticleCFValues ;
DROP TABLE FM_Transactions ;
-DROP TABLE FM_Deltas ;
+DROP TABLE FM_ObjectTopics ;
+DROP TABLE FM_Topics ;
DROP sequence FM_Classes_id_seq;
DROP sequence FM_ClassCustomFields_id_seq;
DROP sequence FM_CustomFields_id_seq;
@@ -13,4 +14,5 @@
DROP sequence FM_CustomFieldValues_id_seq;
DROP sequence FM_ArticleCFValues_id_seq;
DROP sequence FM_Transactions_id_seq;
-DROP sequence FM_Deltas_id_seq;
+DROP sequence FM_ObjectTopics_id_seq;
+DROP sequence FM_Topics_id_seq;
Modified: rtfm/trunk/etc/drop_schema.mysql
==============================================================================
--- rtfm/trunk/etc/drop_schema.mysql (original)
+++ rtfm/trunk/etc/drop_schema.mysql Mon Jul 12 18:31:21 2004
@@ -5,4 +5,6 @@
DROP TABLE FM_CustomFieldValues ;
DROP TABLE FM_ArticleCFValues ;
DROP TABLE FM_Transactions ;
+DROP TABLE FM_ObjectTopics ;
+DROP TABLE FM_Topics ;
DELETE FROM Links;
Modified: rtfm/trunk/etc/schema.Oracle
==============================================================================
--- rtfm/trunk/etc/schema.Oracle (original)
+++ rtfm/trunk/etc/schema.Oracle Mon Jul 12 18:31:21 2004
@@ -1,3 +1,4 @@
+
CREATE SEQUENCE FM_Classes_seq;
CREATE TABLE FM_Classes (
id NUMBER(11,0)
@@ -12,6 +13,7 @@
LastUpdated DATE
);
+
CREATE SEQUENCE FM_ClassCustomFields_seq;
CREATE TABLE FM_ClassCustomFields (
id NUMBER(11,0)
@@ -25,6 +27,7 @@
LastUpdated DATE
);
+
CREATE SEQUENCE FM_CustomFields_seq;
CREATE TABLE FM_CustomFields (
id NUMBER(11,0)
@@ -39,6 +42,7 @@
LastUpdated DATE
);
+
CREATE SEQUENCE FM_Articles_seq;
CREATE TABLE FM_Articles (
id NUMBER(11,0)
@@ -55,6 +59,7 @@
LastUpdated DATE
);
+
CREATE SEQUENCE FM_CustomFieldValues_seq;
CREATE TABLE FM_CustomFieldValues (
id NUMBER(11,0)
@@ -69,6 +74,7 @@
LastUpdated DATE
);
+
CREATE SEQUENCE FM_ArticleCFValues_seq;
CREATE TABLE FM_ArticleCFValues (
id NUMBER(11,0)
@@ -82,6 +88,7 @@
LastUpdated DATE
);
+
CREATE SEQUENCE FM_Transactions_seq;
CREATE TABLE FM_Transactions (
id NUMBER(11,0)
@@ -95,3 +102,25 @@
Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
Created DATE
);
+
+
+CREATE SEQUENCE FM_Topics_seq;
+CREATE TABLE FM_Topics (
+id NUMBER(11,0)
+ CONSTRAINT FM_Topics_key PRIMARY KEY,
+Parent NUMBER(11,0) DEFAULT 0 NOT NULL,
+Name varchar2(255) DEFAULT '' NOT NULL,
+Description varchar2(255) DEFAULT '' NOT NULL,
+ObjectType varchar2(64) DEFAULT '' NOT NULL,
+ObjectId NUMBER(11,0) NOT NULL
+);
+
+
+CREATE SEQUENCE FM_ObjectTopics_seq;
+CREATE TABLE FM_ObjectTopics (
+id NUMBER(11,0)
+ CONSTRAINT FM_ObjectTopics_key PRIMARY KEY,
+Topic NUMBER(11,0) NOT NULL,
+ObjectType varchar2(64) DEFAULT '' NOT NULL,
+ObjectId NUMBER(11,0) NOT NULL
+);
Modified: rtfm/trunk/etc/schema.Pg
==============================================================================
--- rtfm/trunk/etc/schema.Pg (original)
+++ rtfm/trunk/etc/schema.Pg Mon Jul 12 18:31:21 2004
@@ -38,6 +38,8 @@
LastUpdated TIMESTAMP NULL,
PRIMARY KEY (id)
);
+
+
CREATE TABLE FM_Articles (
id SERIAL,
Name varchar(255) NOT NULL DEFAULT '',
@@ -52,6 +54,8 @@
LastUpdated TIMESTAMP NULL,
PRIMARY KEY (id)
);
+
+
CREATE TABLE FM_CustomFieldValues (
id SERIAL,
CustomField int NOT NULL,
@@ -64,6 +68,8 @@
LastUpdated TIMESTAMP NULL,
PRIMARY KEY (id)
);
+
+
CREATE TABLE FM_ArticleCFValues (
id SERIAL,
Article int NOT NULL,
@@ -74,8 +80,9 @@
LastUpdatedBy integer NOT NULL DEFAULT 0,
LastUpdated TIMESTAMP NULL,
PRIMARY KEY (id)
-
);
+
+
CREATE TABLE FM_Transactions (
id SERIAL,
Article integer NOT NULL DEFAULT 0,
@@ -88,3 +95,23 @@
Created TIMESTAMP NULL,
PRIMARY KEY (id)
);
+
+
+CREATE TABLE FM_Topics (
+id SERIAL,
+Parent integer NOT NULL DEFAULT 0,
+Name varchar(255) NOT NULL DEFAULT '',
+Description varchar(255) NOT NULL DEFAULT '',
+ObjectType varchar(64) NOT NULL DEFAULT '',
+ObjectId integer NOT NULL,
+PRIMARY KEY (id)
+);
+
+
+CREATE TABLE FM_ObjectTopics (
+id SERIAL,
+Topic integer NOT NULL,
+ObjectType varchar(64) NOT NULL DEFAULT '',
+ObjectId integer NOT NULL,
+PRIMARY KEY (id)
+);
Modified: rtfm/trunk/etc/schema.mysql
==============================================================================
--- rtfm/trunk/etc/schema.mysql (original)
+++ rtfm/trunk/etc/schema.mysql Mon Jul 12 18:31:21 2004
@@ -38,6 +38,8 @@
LastUpdated DATETIME NULL,
PRIMARY KEY (id)
) TYPE=InnoDB;
+
+
CREATE TABLE FM_Articles (
id INTEGER NOT NULL AUTO_INCREMENT,
Name varchar(255) NOT NULL DEFAULT '',
@@ -52,6 +54,8 @@
LastUpdated DATETIME NULL,
PRIMARY KEY (id)
) TYPE=InnoDB;
+
+
CREATE TABLE FM_CustomFieldValues (
id INTEGER NOT NULL AUTO_INCREMENT,
CustomField int NOT NULL,
@@ -64,6 +68,8 @@
LastUpdated DATETIME NULL,
PRIMARY KEY (id)
) TYPE=InnoDB;
+
+
CREATE TABLE FM_ArticleCFValues (
id INTEGER NOT NULL AUTO_INCREMENT,
Article int NOT NULL,
@@ -74,8 +80,9 @@
LastUpdatedBy integer NOT NULL DEFAULT 0,
LastUpdated DATETIME NULL,
PRIMARY KEY (id)
-
) TYPE=InnoDB;
+
+
CREATE TABLE FM_Transactions (
id INTEGER NOT NULL AUTO_INCREMENT,
Article integer NOT NULL DEFAULT 0,
@@ -88,3 +95,23 @@
Created DATETIME NULL,
PRIMARY KEY (id)
) TYPE=InnoDB;
+
+
+CREATE TABLE FM_Topics (
+id INTEGER NOT NULL AUTO_INCREMENT,
+Parent integer NOT NULL DEFAULT 0,
+Name varchar(255) NOT NULL DEFAULT '',
+Description varchar(255) NOT NULL DEFAULT '',
+ObjectType varchar(64) NOT NULL DEFAULT '',
+ObjectId integer NOT NULL,
+PRIMARY KEY (id)
+) TYPE=InnoDB;
+
+
+CREATE TABLE FM_ObjectTopics (
+id INTEGER NOT NULL AUTO_INCREMENT,
+Topic integer NOT NULL,
+ObjectType varchar(64) NOT NULL DEFAULT '',
+ObjectId integer NOT NULL,
+PRIMARY KEY (id)
+) TYPE=InnoDB;
Modified: rtfm/trunk/html/Callbacks/RTFM/autohandler/Default
==============================================================================
--- rtfm/trunk/html/Callbacks/RTFM/autohandler/Default (original)
+++ rtfm/trunk/html/Callbacks/RTFM/autohandler/Default Mon Jul 12 18:31:21 2004
@@ -21,6 +21,8 @@
use RT::FM::ArticleCollection;
use RT::FM::ClassCollection;
use RT::FM::CustomFieldCollection;
+use RT::FM::TopicCollection;
+use RT::FM::ObjectTopicCollection;
use Time::ParseDate;
return(1);
</%init>
Added: rtfm/trunk/html/RTFM/Admin/Classes/Topics.html
==============================================================================
--- (empty file)
+++ rtfm/trunk/html/RTFM/Admin/Classes/Topics.html Mon Jul 12 18:31:21 2004
@@ -0,0 +1,188 @@
+%# 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
+
+<& /RTFM/Admin/Elements/ClassTabs, id => $ClassObj->id, Title => $title, current_tab => "RTFM/Admin/Classes/Topics.html?id=$id" &>
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="<%$RT::WebPath%>/RTFM/Admin/Classes/Topics.html" method="POST">
+<input type="hidden" name="id" value="<%$ClassObj->Id%>">
+
+% if (!$Modify) {
+<table>
+<tr>
+<td><&|/l&>Topic Name</&></td>
+<td><input type="text" name="Name" size="30" /></td>
+</tr>
+<tr>
+<td><&|/l&>Description</&></td>
+<td><input type="text" name="Description" size="50" /></td>
+</tr>
+</table>
+% } else {
+<a href="Topics.html">New topic</a>
+% }
+
+<& .tree, Element => $tree, Action => $Modify ? "Move" : "Add", Prefix => $Modify ? "Topic-$Modify-Parent" : "Insert", ClassObj => $ClassObj, Modify => $Modify &>
+
+</form>
+
+<%def .edit>
+
+<table style="margin-top: -0.75em">
+<tr><td>
+<input type="text" name="Topic-<%$topic->Id%>-Name" size="20" value="<%$topic->Name%>" /><br />
+<input type="text" name="Topic-<%$topic->Id%>-Description" size="20" value="<%$topic->Description%>" />
+</td><td>
+<input type="submit" name="Update" value="Update"><br />
+<input type="submit" name="Delete-Topic-<%$topic->Id%>" value="Delete" />
+</td></tr>
+</table>
+<%args>
+$topic
+</%args>
+</%def>
+
+<%def .tree>
+% my $topic = $Element->getNodeValue;
+% unless ($Element->isRoot) {
+% if ($Modify and $topic->Id == $Modify) {
+% $Action = "";
+<& .edit, topic => $topic &>
+% } else {
+<a href="<%$RT::WebPath%>/RTFM/Admin/Classes/Topics.html?id=<%$ClassObj->Id%>&Modify=<%$topic->Id%>"
+ title="<%$topic->Description%>"><%$topic->Name || loc("(no name)") %></a>
+% }
+% }
+<ul>
+% for my $e (sort {$a->getNodeValue->Name cmp $b->getNodeValue->Name} $Element->getAllChildren) {
+<li><& .tree, Element => $e, Action => $Action, Prefix => $Prefix, ClassObj => $ClassObj, Modify => $Modify &></li>
+% }
+% if ($Action) {
+<li><input type="submit" name="<%$Prefix%>-<%$topic eq "root" ? 0 : $topic->Id%>" value="<&|/l&><%$Action%> here</&>" /></li>
+% }
+</ul>
+<%args>
+$Element
+$Action
+$Prefix
+$ClassObj
+$Modify
+</%args>
+</%def>
+</%def>
+
+<%INIT>
+
+my $ClassObj = new RT::FM::Class($session{'CurrentUser'});
+$ClassObj->Load($ARGS{'id'}) || $m->comp("/RTFM/Elements/Error", Why => "Couldn't load class '$id'");
+
+my $title = $Modify
+ ? loc("Modify topic for [_1]", $ClassObj->Name)
+ : loc("Edit topic hierarchy for [_1]", $ClassObj->Name);
+
+my @results;
+
+for my $k (keys %ARGS) {
+ if ($k =~ /^Topic-(\d+)-(Name|Description)/) {
+ my $topic = RT::FM::Topic->new($session{'CurrentUser'});
+ $topic->Load($1);
+ if ($topic->Id) {
+ next if $ARGS{$k} eq $topic->$2;
+ my $proc = "Set$2";
+ my ($val, $msg) = $topic->$proc($ARGS{$k});
+ push @results, $msg;
+ } else {
+ $m->comp("/Elements/Error", Why => loc("Topic not found"));
+ }
+ } elsif ($k =~ /^Topic-(\d+)-Parent-(\d+)/) {
+ my $topic = RT::FM::Topic->new($session{'CurrentUser'});
+ $topic->Load($1);
+ if ($topic->Id) {
+ next if $2 eq $topic->Parent;
+ my $old = $topic->Parent;
+ my $new = "$2";
+ my ($val, $msg) = $topic->setParent($new);
+ push @results, $msg;
+ } else {
+ $m->comp("/Elements/Error", Why => loc("Topic not found"));
+ }
+ } elsif ($k =~ /^Insert-(\d+)/) {
+ my $topic = RT::FM::Topic->new($session{'CurrentUser'});
+ my ($id, $msg) = $topic->Create(
+ Parent => $1,
+ Name => $ARGS{'Name'},
+ Description => $ARGS{'Description'},
+ ObjectType => ref($ClassObj),
+ ObjectId => $ClassObj->Id,
+ );
+ push @results, $msg;
+ }
+}
+for my $k (keys %ARGS) {
+ next unless $k =~ /^Delete-Topic-(\d+)/;
+ my $topic = RT::FM::Topic->new($session{'CurrentUser'});
+ $topic->Load($1);
+ if ($topic->Id) {
+ my ($val, $msg) = $topic->DeleteAll();
+ push @results, $msg;
+ } else {
+ $m->comp("/Elements/Error", Why => loc("Topic not found"));
+ }
+}
+
+my $topics = new RT::FM::TopicCollection($session{'CurrentUser'});
+$topics->LimitToObject($ClassObj);
+$topics->OrderByCols({FIELD => 'Parent'}, {FIELD => 'id'});
+
+use Tree::Simple;
+my $tree = Tree::Simple->new(Tree::Simple->ROOT);
+my %lookup = (0 => $tree);
+
+my @todo;
+while (my $topic = $topics->Next) {
+ push @todo, $topic;
+}
+
+{
+ my $changed = 0;
+ my @work = @todo;
+ @todo = ();
+ for my $topic (@work) {
+ if (defined $lookup{$topic->Parent}) {
+ $lookup{$topic->Id} = Tree::Simple->new($topic, $lookup{$topic->Parent});
+ $changed = 1;
+ } else {
+ push @todo, $topic;
+ }
+ }
+ redo unless $changed == 0;
+}
+
+for my $topic (@todo) {
+ $topic->setParent(0);
+ $lookup{$topic->Id} = Tree::Simple->new($topic, $tree);
+ push @results, "Reparented orphan ".$topic->Id." to root";
+}
+
+</%INIT>
+
+
+<%ARGS>
+$id => undef
+$Modify => ""
+</%ARGS>
Modified: rtfm/trunk/html/RTFM/Admin/Elements/ClassTabs
==============================================================================
--- rtfm/trunk/html/RTFM/Admin/Elements/ClassTabs (original)
+++ rtfm/trunk/html/RTFM/Admin/Elements/ClassTabs Mon Jul 12 18:31:21 2004
@@ -28,22 +28,24 @@
$tabs = {
D => { title => loc('Basics'),
path => "RTFM/Admin/Classes/Modify.html?id=".$id,
- },
-
- E => { title => loc('Custom Fields'),
- path => 'RTFM/Admin/Classes/CustomFields.html?id='.$id,
- },
+ },
+
+ E => { title => loc('Topics'),
+ path => "RTFM/Admin/Classes/Topics.html?id=".$id,
+ },
- F => { title => loc('Group Rights'),
- path => "RTFM/Admin/Classes/GroupRights.html?id=".$id,
- },
- G => { title => loc('User Rights'),
- path => "RTFM/Admin/Classes/UserRights.html?id=".$id,
- },
+ F => { title => loc('Custom Fields'),
+ path => 'RTFM/Admin/Classes/CustomFields.html?id='.$id,
+ },
+ G => { title => loc('Group Rights'),
+ path => "RTFM/Admin/Classes/GroupRights.html?id=".$id,
+ },
-
-};
+ H => { title => loc('User Rights'),
+ path => "RTFM/Admin/Classes/UserRights.html?id=".$id,
+ },
+ };
}
if ($session{'CurrentUser'}->HasRight( Object => $RT::FM::System, Right => 'AdminClass')) {
$tabs->{"A"} = { title => loc('Select class'),
Modified: rtfm/trunk/html/RTFM/Article/Display.html
==============================================================================
--- rtfm/trunk/html/RTFM/Article/Display.html (original)
+++ rtfm/trunk/html/RTFM/Article/Display.html Mon Jul 12 18:31:21 2004
@@ -24,6 +24,8 @@
<br><br><br>
<& Elements/ShowCustomFields, article => $article &>
<br>
+<& Elements/ShowTopics, article => $article &>
+<br>
<& Elements/ShowLinks, article => $article &>
<%init>
Modified: rtfm/trunk/html/RTFM/Article/Edit.html
==============================================================================
--- rtfm/trunk/html/RTFM/Article/Edit.html (original)
+++ rtfm/trunk/html/RTFM/Article/Edit.html Mon Jul 12 18:31:21 2004
@@ -33,14 +33,18 @@
<& Elements/EditBasics, ArticleObj => $ArticleObj,
EditClass =>$EditClass,
- ClassObj =>$ClassObj,
+ ClassObj => $ClassObj,
id => $id,
- %ARGS &>
+ %ARGS &>
<& Elements/EditCustomFields, ArticleObj => $ArticleObj,
CFContent => \%CFContent,
ClassObj => $ClassObj,
id =>$id,
%ARGS &>
+<& Elements/EditTopics, ArticleObj => $ArticleObj,
+ ClassObj => $ArticleObj->Id ? $ArticleObj->ClassObj : $ClassObj,
+ id => $id,
+ %ARGS &>
<& Elements/EditLinks, ArticleObj => $ArticleObj,
id => $id,
%ARGS &>
@@ -51,6 +55,10 @@
</form>
<%INIT>
+if (exists $ARGS{'Topics'}) {
+ $ARGS{'Topics'} = ref($ARGS{'Topics'}) ? $ARGS{'Topics'} : [$ARGS{'Topics'}]
+}
+
my @results;
my $title;
@@ -99,6 +107,7 @@
( $id, $msg ) = $ArticleObj->Create( Summary => $ARGS{'Summary'},
Name => $ARGS{'Name'},
Class => $ARGS{'Class'},
+ Topics => $ARGS{'Topics'},
%create_args );
push ( @results, $msg );
if ($id ) {
@@ -261,6 +270,26 @@
}
+ my %topics;
+ if ($ARGS{'EditTopics'}) {
+ $topics{$_}++ for @{$ARGS{'Topics'}};
+ my $objTopics = new RT::FM::ObjectTopicCollection($session{'CurrentUser'});
+ $objTopics->LimitToObject($ArticleObj);
+ while (my $t = $objTopics->Next) {
+ $topics{$t->Topic}--;
+ }
+ for my $id (keys %topics) {
+ if ($topics{$id} > 0) {
+ my ($val, $msg) = $ArticleObj->AddTopic(Topic => $id);
+ push @results, $msg;
+ } elsif ($topics{$id} < 0) {
+ my ($val, $msg) = $ArticleObj->DeleteTopic(Topic => $id);
+ push @results, $msg;
+ }
+ }
+ }
+
+
$title = loc( 'Modify article #[_1]', $ArticleObj->Id );
}
Added: rtfm/trunk/html/RTFM/Article/Elements/EditTopics
==============================================================================
--- (empty file)
+++ rtfm/trunk/html/RTFM/Article/Elements/EditTopics Mon Jul 12 18:31:21 2004
@@ -0,0 +1,109 @@
+%# 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
+
+<table>
+<tr>
+<td>Fields in this class:</td>
+<td>
+
+<input type="hidden" name="EditTopics" value="1" />
+<select multiple size="10" name="Topics">
+ <optgroup label="Current class (<% $ClassObj->Name%>)">
+% $inTree->traverse(sub {
+% my $tree = shift;
+% my $topic = $tree->getNodeValue;
+% $m->print("<option value=\"".$topic->Id."\""
+% .(exists $topics{$topic->Id} ? " selected" : "").">"
+% .(" " x ($tree->getDepth*5)).$topic->Name."</option>\n");
+% });
+% my $class = $ClassObj->Id;
+% $otherTree->traverse(sub {
+% my $tree = shift;
+% my $topic = $tree->getNodeValue;
+% if ($topic->ObjectId != $class) {
+% $class = $topic->ObjectId;
+% $m->print("</optgroup>\n");
+% my $c = new RT::FM::Class($session{'CurrentUser'});
+% $c->Load($topic->ObjectId);
+% $m->print("<optgroup label=\"".$c->Name."\">\n");
+% }
+% $m->print("<option value=\"".$topic->Id."\""
+% .(exists $topics{$topic->Id} ? " selected" : "").">"
+% .(" " x ($tree->getDepth*5)).$topic->Name."</option>\n");
+% });
+</optgroup>
+</select>
+</td>
+</tr>
+
+<%INIT>
+use Tree::Simple;
+
+my $inClass = new RT::FM::TopicCollection($session{'CurrentUser'});
+$inClass->LimitToObject($ClassObj);
+$inClass->OrderByCols({FIELD => 'Name'});
+my $inTree = buildTree($inClass);
+
+my $otherClass = new RT::FM::TopicCollection($session{'CurrentUser'});
+$otherClass->Limit(FIELD => 'ObjectType', VALUE => 'RT::FM::Class');
+$otherClass->Limit(FIELD => 'ObjectId', OPERATOR => '!=', VALUE => $ClassObj->Id);
+my $otherTree = buildTree($otherClass);
+
+my $articleTopics = new RT::FM::ObjectTopicCollection($session{'CurrentUser'});
+$articleTopics->LimitToObject($ArticleObj);
+my %topics;
+while (my $topicObj = $articleTopics->Next) {
+ $topics{$topicObj->Topic} = $topicObj;
+}
+
+
+sub buildTree {
+ my $query = shift;
+
+ use Tree::Simple;
+ my $tree = Tree::Simple->new(Tree::Simple->ROOT);
+ my %lookup = (0 => $tree);
+
+ my @todo;
+ while (my $topic = $query->Next) {
+ push @todo, $topic;
+ }
+
+ {
+ my $changed = 0;
+ my @work = @todo;
+ @todo = ();
+ for my $topic (@work) {
+ if (defined $lookup{$topic->Parent}) {
+ $lookup{$topic->Id} = Tree::Simple->new($topic, $lookup{$topic->Parent});
+ $changed = 1;
+ } else {
+ push @todo, $topic;
+ }
+ }
+ redo unless $changed == 0;
+ }
+ return $tree;
+}
+
+</%INIT>
+<%ARGS>
+$id => undef
+$ArticleObj => undef
+$ClassObj => undef
+</%ARGS>
Added: rtfm/trunk/html/RTFM/Article/Elements/ShowTopics
==============================================================================
--- (empty file)
+++ rtfm/trunk/html/RTFM/Article/Elements/ShowTopics Mon Jul 12 18:31:21 2004
@@ -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
+
+Topics:
+% my $topics = new RT::FM::ObjectTopicCollection($session{'CurrentUser'});
+% $topics->LimitToObject($article);
+% if ($topics->Count) {
+<ul>
+% my @topics;
+% while (my $t = $topics->Next) {
+% my @path;
+% my $t = $t->TopicObj;
+% while ($t->Id) {
+% unshift @path, $t->Name;
+% $t = $t->ParentObj;
+% }
+% push @topics, join(" : ", @path);
+% }
+% for (sort @topics) {
+<li><% $_ %></li>
+% }
+</ul>
+% } else {
+<i>no topics</i>
+% }
+
+<%args>
+$article => undef
+</%args>
Modified: rtfm/trunk/html/RTFM/Article/PreCreate.html
==============================================================================
--- rtfm/trunk/html/RTFM/Article/PreCreate.html (original)
+++ rtfm/trunk/html/RTFM/Article/PreCreate.html Mon Jul 12 18:31:21 2004
@@ -16,14 +16,13 @@
%#
%# END LICENSE BLOCK
-<& /RTFM/Article/Elements/Tabs, current_tab => "RTFM/Article/PreCreate.html",
+<& /RTFM/Article/Elements/Tabs, current_tab => "RTFM/Topics.html",
Title => loc('Create an article in class...') &>
<ul>
% my $Classes = RT::FM::ClassCollection->new($session{'CurrentUser'});
% $Classes->UnLimit;
% while (my $Class = $Classes->Next) {
-<li><a href="Edit.html?Class=<%$Class->Id%>"><&|/l, $Class->Name&>in class [_1]</&>
-</a></li>
+<li><a href="Edit.html?Class=<%$Class->Id%>"><&|/l, $Class->Name&>in class [_1]</&></a></li>
% }
</ul>
Modified: rtfm/trunk/html/RTFM/Elements/Tabs
==============================================================================
--- rtfm/trunk/html/RTFM/Elements/Tabs (original)
+++ rtfm/trunk/html/RTFM/Elements/Tabs Mon Jul 12 18:31:21 2004
@@ -39,12 +39,14 @@
};
my $second_tabs = { 'aab' => { title => loc('Overview'),
- path => 'RTFM/index.html' },
- 'articles' => { title => loc('Articles'),
- path => 'RTFM/Article/Search.html' },
- 'd' => { title => loc('Configuration'),
- path => 'RTFM/Admin/index.html' }
- };
+ path => 'RTFM/index.html' },
+ 'articles' => { title => loc('Articles'),
+ path => 'RTFM/Article/Search.html' },
+ 'atopics' => { title => loc('Topics'),
+ path => 'RTFM/Topics.html'},
+ 'd' => { title => loc('Configuration'),
+ path => 'RTFM/Admin/index.html' }
+ };
my $topactions = {
B => { html => $m->scomp('/RTFM/Elements/GotoArticle') }
Added: rtfm/trunk/html/RTFM/Topics.html
==============================================================================
--- (empty file)
+++ rtfm/trunk/html/RTFM/Topics.html Mon Jul 12 18:31:21 2004
@@ -0,0 +1,89 @@
+%# 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
+
+<& /RTFM/Elements/Tabs, current_toptab => "RTFM/Topics.html", Title => loc('Topic hierarchy') &>
+
+% if (defined $class) {
+
+% if ($id != 0) {
+% my $t = RT::FM::Topic->new($session{'CurrentUser'});
+% $t->Load($id);
+<a href="Topics.html?class=<% $class %>&id=<% $t->Parent %>">Parent</a><br />
+% }
+
+<& /Elements/TitleBoxStart, title => loc('Topics') &>
+% my $Topics = RT::FM::TopicCollection->new($session{'CurrentUser'});
+% $Topics->Limit(FIELD => 'ObjectType', VALUE => 'RT::FM::Class');
+% $Topics->Limit(FIELD => 'ObjectId', VALUE => $class);
+% $Topics->Limit(FIELD => 'Parent', VALUE => $id);
+% if ($Topics->Count) {
+<ul>
+% while (my $t = $Topics->Next) {
+<li><a href="Topics.html?class=<% $class %>&id=<% $t->Id %>"><% $t->Name %></a></li>
+% }
+</ul>
+% } else {
+<i>No sub-topics</i>
+% }
+<& /Elements/TitleBoxEnd &>
+
+<br />
+
+% if ($id != 0) {
+<& /Elements/TitleBoxStart, title => loc('Articles') &>
+% my $Articles = RT::FM::ObjectTopicCollection->new($session{'CurrentUser'});
+% $Articles->Limit(FIELD => 'Topic', VALUE => $id);
+% $Articles->Limit(FIELD => 'ObjectType', VALUE => 'RT::FM::Article');
+% if ($Articles->Count) {
+<ul>
+% while (my $obj = $Articles->Next) {
+% my $a = RT::FM::Article->new($session{'CurrentUser'});
+% $a->Load($obj->ObjectId);
+<li><a href="Article/Display.html?id=<% $a->Id %>"><% $a->Name %></a></li>
+% }
+</ul>
+% } else {
+<i>No articles in this topic</i>
+% }
+<& /Elements/TitleBoxEnd &>
+% }
+
+% } else {
+Choose a class to examine:
+<ul>
+% my $Classes = RT::FM::ClassCollection->new($session{'CurrentUser'});
+% $Classes->UnLimit;
+% while (my $c = $Classes->Next) {
+<li><a href="Topics.html?class=<%$c->Id%>"><% $c->Name %></li>
+% }
+</ul>
+
+% }
+
+<%init>
+my $Classes;
+if (defined $class) {
+
+} else {
+}
+</%init>
+
+<%args>
+$id => 0
+$class => undef
+</%args>
Modified: rtfm/trunk/lib/RT/FM/ArticleCollection_Overlay.pm
==============================================================================
--- rtfm/trunk/lib/RT/FM/ArticleCollection_Overlay.pm (original)
+++ rtfm/trunk/lib/RT/FM/ArticleCollection_Overlay.pm Mon Jul 12 18:31:21 2004
@@ -200,6 +200,7 @@
# {{{ LimitToParent ID
+
=item LimitToParent ID
Limit the returned set of articles to articles which are children
Modified: rtfm/trunk/lib/RT/FM/Article_Overlay.pm
==============================================================================
--- rtfm/trunk/lib/RT/FM/Article_Overlay.pm (original)
+++ rtfm/trunk/lib/RT/FM/Article_Overlay.pm Mon Jul 12 18:31:21 2004
@@ -91,9 +91,6 @@
Links => {},
@_ );
- use Data::Dumper;
- $RT::Logger->crit(Dumper \%args);
-
my $class = RT::FM::Class->new($RT::SystemUser);
$class->Load( $args{'Class'} );
unless ( $class->Id ) {
@@ -139,6 +136,18 @@
}
# }}}
+ # {{{ Add topics
+
+ foreach my $topic (@{$args{Topics}}) {
+ my ( $cfid, $cfmsg ) = $self->AddTopic(Topic => $topic);
+
+ unless ($cfid) {
+ $RT::Handle->Rollback();
+ return ( undef, $cfmsg );
+ }
+ }
+
+ # }}}
# {{{ Add relationships
foreach my $type ( keys %args ) {
@@ -291,6 +300,7 @@
my $linksfrom = $self->_Links( Field => 'Base' );
my $cfvalues = $self->CustomFieldValues;
my $txns = $self->Transactions;
+ my $topics = $self->Topics;
while ( my $item = $linksto->Next ) {
my ( $val, $msg ) = $item->Delete();
@@ -309,6 +319,7 @@
return ( 0, $self->loc('Internal Error') );
}
}
+
while ( my $item = $txns->Next ) {
my ( $val, $msg ) = $item->Delete();
unless ($val) {
@@ -327,6 +338,15 @@
}
}
+ while (my $item = $topics->Next) {
+ my ($val, $msg ) = $item->Delete();
+ unless ($val) {
+ $RT::Logger->crit( ref($item) . ": $msg" );
+ $RT::Handle->Rollback();
+ return ( 0, $self->loc('Internal Error') );
+ }
+ }
+
$self->SUPER::Delete();
$RT::Handle->Commit();
return ( 1, $self->loc('Article Deleted') );
@@ -1062,9 +1082,9 @@
# {{{ DeleteCustomFieldValue
=item DeleteCustomFieldValue { CustomField => undef, Content => undef }
-
- Takes a paramhash. Deletes the Keyword denoted by the I<Keyword> parameter from this
- ticket's object keywords.
+
+Takes a paramhash. Deletes the Keyword denoted by the I<Keyword>
+parameter from this ticket's object keywords.
=cut
@@ -1130,6 +1150,70 @@
# }}}
+# {{{ Topics
+
+# {{{ Topics
+sub Topics {
+ my $self = shift;
+
+ my $topics = new RT::FM::ObjectTopicCollection($self->CurrentUser);
+ if ($self->CurrentUserHasRight('ShowArticle')) {
+ $topics->LimitToObject($self);
+ }
+ return $topics;
+}
+# }}}
+
+# {{{ AddTopic
+sub AddTopic {
+ my $self = shift;
+ my %args = ( @_ );
+
+ unless ( $self->CurrentUserHasRight('ModifyArticleTopics') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ my $t = new RT::FM::ObjectTopic($self->CurrentUser);
+ my ($tid) = $t->Create( Topic => $args{'Topic'},
+ ObjectType => ref($self),
+ ObjectId => $self->Id );
+ if ($tid) {
+ return ($tid, $self->loc("Topic membership added"));
+ } else {
+ return (0, $self->loc("Unable to add topic membership"));
+ }
+}
+# }}}
+
+# {{{ DeleteTopic
+sub DeleteTopic {
+ my $self = shift;
+ my %args = ( @_ );
+
+ unless ( $self->CurrentUserHasRight('ModifyArticleTopics') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ my $t = new RT::FM::ObjectTopic($self->CurrentUser);
+ $t->LoadByCols(Topic => $args{'Topic'}, ObjectId => $self->Id, ObjectType => ref($self));
+ if ($t->Id) {
+ my $del = $t->Delete;
+ unless ($del) {
+ return ( undef,
+ $self->loc("Unable to delete topic membership in [_1]",
+ $t->TopicObj->Name));
+ } else {
+ return ( 1,
+ $self->loc("Topic membership removed"));
+ }
+ } else {
+ return ( undef,
+ $self->loc("Couldn't load topic membership while trying to delete it"));
+ }
+}
+# }}}
+# }}}
+
# {{{ CurrentUserHasRight
=head2 CurrentUserHasRight
Modified: rtfm/trunk/lib/RT/FM/Class_Overlay.pm
==============================================================================
--- rtfm/trunk/lib/RT/FM/Class_Overlay.pm (original)
+++ rtfm/trunk/lib/RT/FM/Class_Overlay.pm Mon Jul 12 18:31:21 2004
@@ -52,12 +52,14 @@
SeeClass => 'See that this class exists', #loc_pair
CreateArticle => 'Create articles in this class', #loc_pair
- ShowArticle => 'See articles in this class', #loc_pair
- ShowArticleHistory => 'See articles in this class', #loc_pair
+ ShowArticle => 'See articles in this class', #loc_pair
+ ShowArticleHistory => 'See articles in this class', #loc_pair
ModifyArticle => 'Modify or delete articles in this class', #loc_pair
+ ModifyArticleTopics => 'Modify topics for articles in this class', #loc_pair
AdminClass => 'Modify metadata and custom fields for this class', #loc_pair
- ShowACL => 'Display Access Control List', # loc_pair
- ModifyACL => 'Modify Access Control List', # loc_pair
+ AdminTopics => 'Modify topic hierarchy associated with this class', #loc_pair
+ ShowACL => 'Display Access Control List', # loc_pair
+ ModifyACL => 'Modify Access Control List', # loc_pair
};
# TODO: This should be refactored out into an RT::ACLedObject or something
Added: rtfm/trunk/lib/RT/FM/ObjectTopic.pm
==============================================================================
--- (empty file)
+++ rtfm/trunk/lib/RT/FM/ObjectTopic.pm Mon Jul 12 18:31:21 2004
@@ -0,0 +1,199 @@
+
+
+
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse at bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+
+=head1 NAME
+
+RT::FM::ObjectTopic
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::FM::ObjectTopic;
+use RT::FM::Record;
+use RT::FM::Topic;
+
+
+use base qw( RT::FM::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('FM_ObjectTopics');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=item Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ int(11) 'Topic'.
+ varchar(64) 'ObjectType'.
+ int(11) 'ObjectId'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Topic => '0',
+ ObjectType => '',
+ ObjectId => '0',
+
+ @_);
+ $self->SUPER::Create(
+ Topic => $args{'Topic'},
+ ObjectType => $args{'ObjectType'},
+ ObjectId => $args{'ObjectId'},
+);
+
+}
+
+
+
+=item id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=item Topic
+
+Returns the current value of Topic.
+(In the database, Topic is stored as int(11).)
+
+
+
+=item SetTopic VALUE
+
+
+Set Topic to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Topic will be stored as a int(11).)
+
+
+=cut
+
+
+=item TopicObj
+
+Returns the Topic Object which has the id returned by Topic
+
+
+=cut
+
+sub TopicObj {
+ my $self = shift;
+ my $Topic = RT::FM::Topic->new($self->CurrentUser);
+ $Topic->Load($self->Topic());
+ return($Topic);
+}
+
+=item ObjectType
+
+Returns the current value of ObjectType.
+(In the database, ObjectType is stored as varchar(64).)
+
+
+
+=item SetObjectType VALUE
+
+
+Set ObjectType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectType will be stored as a varchar(64).)
+
+
+=cut
+
+
+=item ObjectId
+
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
+
+
+
+=item SetObjectId VALUE
+
+
+Set ObjectId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectId will be stored as a int(11).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, type => 'int(11)', default => ''},
+ Topic =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ ObjectType =>
+ {read => 1, write => 1, type => 'varchar(64)', default => ''},
+ ObjectId =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+
+ }
+};
+
+
+ eval "require RT::FM::ObjectTopic_Overlay";
+ if ($@ && $@ !~ /^Can't locate/) {
+ die $@;
+ };
+
+ eval "require RT::FM::ObjectTopic_Local";
+ if ($@ && $@ !~ /^Can't locate/) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::FM::ObjectTopic_Overlay, RT::FM::ObjectTopic_Local
+
+=cut
+
+
+1;
Added: rtfm/trunk/lib/RT/FM/ObjectTopicCollection.pm
==============================================================================
--- (empty file)
+++ rtfm/trunk/lib/RT/FM/ObjectTopicCollection.pm Mon Jul 12 18:31:21 2004
@@ -0,0 +1,85 @@
+
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse at bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+
+=head1 NAME
+
+ RT::FM::ObjectTopicCollection -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::FM::ObjectTopicCollection
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::FM::ObjectTopicCollection;
+
+use RT::FM::SearchBuilder;
+use RT::FM::ObjectTopic;
+
+use base qw(RT::FM::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'FM_ObjectTopics';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=item NewItem
+
+Returns an empty new RT::FM::ObjectTopic item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::FM::ObjectTopic->new($self->CurrentUser));
+}
+
+ eval "require RT::FM::ObjectTopicCollection_Overlay";
+ if ($@ && $@ !~ /^Can't locate/) {
+ die $@;
+ };
+
+ eval "require RT::FM::ObjectTopicCollection_Local";
+ if ($@ && $@ !~ /^Can't locate/) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::FM::ObjectTopicCollection_Overlay, RT::FM::ObjectTopicCollection_Local
+
+=cut
+
+
+1;
Added: rtfm/trunk/lib/RT/FM/ObjectTopicCollection_Overlay.pm
==============================================================================
--- (empty file)
+++ rtfm/trunk/lib/RT/FM/ObjectTopicCollection_Overlay.pm Mon Jul 12 18:31:21 2004
@@ -0,0 +1,68 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse at bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# 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.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+use strict;
+no warnings qw(redefine);
+
+# {{{ LimitToTopic
+
+=head2 LimitToTopic FIELD
+
+Returns values for the topic with Id FIELD
+
+=cut
+
+sub LimitToTopic {
+ my $self = shift;
+ my $cf = shift;
+ return ($self->Limit( FIELD => 'Topic',
+ VALUE => $cf,
+ OPERATOR => '='));
+
+}
+
+# }}}
+
+
+# {{{ LimitToObject
+
+=head2 LimitToObject OBJ
+
+Returns associations for the given OBJ only
+
+=cut
+
+sub LimitToObject {
+ my $self = shift;
+ my $object = shift;
+
+ $self->Limit( FIELD => 'ObjectType',
+ VALUE => ref($object));
+ $self->Limit( FIELD => 'ObjectId',
+ VALUE => $object->Id);
+
+}
+
+# }}}
+
+1;
Added: rtfm/trunk/lib/RT/FM/Topic.pm
==============================================================================
--- (empty file)
+++ rtfm/trunk/lib/RT/FM/Topic.pm Mon Jul 12 18:31:21 2004
@@ -0,0 +1,230 @@
+
+
+
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse at bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+
+=head1 NAME
+
+RT::FM::Topic
+
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+=head1 METHODS
+
+=cut
+
+package RT::FM::Topic;
+use RT::FM::Record;
+
+
+use base qw( RT::FM::Record );
+
+sub _Init {
+ my $self = shift;
+
+ $self->Table('FM_Topics');
+ $self->SUPER::_Init(@_);
+}
+
+
+
+
+
+=item Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ int(11) 'Parent'.
+ varchar(255) 'Name'.
+ varchar(255) 'Description'.
+ varchar(64) 'ObjectType'.
+ int(11) 'ObjectId'.
+
+=cut
+
+
+
+
+sub Create {
+ my $self = shift;
+ my %args = (
+ Parent => '',
+ Name => '',
+ Description => '',
+ ObjectType => '',
+ ObjectId => '0',
+
+ @_);
+ $self->SUPER::Create(
+ Parent => $args{'Parent'},
+ Name => $args{'Name'},
+ Description => $args{'Description'},
+ ObjectType => $args{'ObjectType'},
+ ObjectId => $args{'ObjectId'},
+);
+
+}
+
+
+
+=item id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=item Parent
+
+Returns the current value of Parent.
+(In the database, Parent is stored as int(11).)
+
+
+
+=item SetParent VALUE
+
+
+Set Parent to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Parent will be stored as a int(11).)
+
+
+=cut
+
+
+=item Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(255).)
+
+
+
+=item SetName VALUE
+
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(255).)
+
+
+=cut
+
+
+=item Description
+
+Returns the current value of Description.
+(In the database, Description is stored as varchar(255).)
+
+
+
+=item SetDescription VALUE
+
+
+Set Description to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Description will be stored as a varchar(255).)
+
+
+=cut
+
+
+=item ObjectType
+
+Returns the current value of ObjectType.
+(In the database, ObjectType is stored as varchar(64).)
+
+
+
+=item SetObjectType VALUE
+
+
+Set ObjectType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectType will be stored as a varchar(64).)
+
+
+=cut
+
+
+=item ObjectId
+
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
+
+
+
+=item SetObjectId VALUE
+
+
+Set ObjectId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectId will be stored as a int(11).)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+ {
+
+ id =>
+ {read => 1, type => 'int(11)', default => ''},
+ Parent =>
+ {read => 1, write => 1, type => 'int(11)', default => ''},
+ Name =>
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
+ Description =>
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
+ ObjectType =>
+ {read => 1, write => 1, type => 'varchar(64)', default => ''},
+ ObjectId =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+
+ }
+};
+
+
+ eval "require RT::FM::Topic_Overlay";
+ if ($@ && $@ !~ /^Can't locate/) {
+ die $@;
+ };
+
+ eval "require RT::FM::Topic_Local";
+ if ($@ && $@ !~ /^Can't locate/) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::FM::Topic_Overlay, RT::FM::Topic_Local
+
+=cut
+
+
+1;
Added: rtfm/trunk/lib/RT/FM/TopicCollection.pm
==============================================================================
--- (empty file)
+++ rtfm/trunk/lib/RT/FM/TopicCollection.pm Mon Jul 12 18:31:21 2004
@@ -0,0 +1,85 @@
+
+# Autogenerated by DBIx::SearchBuilder factory (by <jesse at bestpractical.com>)
+# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
+#
+# !! DO NOT EDIT THIS FILE !!
+#
+
+
+=head1 NAME
+
+ RT::FM::TopicCollection -- Class Description
+
+=head1 SYNOPSIS
+
+ use RT::FM::TopicCollection
+
+=head1 DESCRIPTION
+
+
+=head1 METHODS
+
+=cut
+
+package RT::FM::TopicCollection;
+
+use RT::FM::SearchBuilder;
+use RT::FM::Topic;
+
+use base qw(RT::FM::SearchBuilder);
+
+
+sub _Init {
+ my $self = shift;
+ $self->{'table'} = 'FM_Topics';
+ $self->{'primary_key'} = 'id';
+
+
+ return ( $self->SUPER::_Init(@_) );
+}
+
+
+=item NewItem
+
+Returns an empty new RT::FM::Topic item
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return(RT::FM::Topic->new($self->CurrentUser));
+}
+
+ eval "require RT::FM::TopicCollection_Overlay";
+ if ($@ && $@ !~ /^Can't locate/) {
+ die $@;
+ };
+
+ eval "require RT::FM::TopicCollection_Local";
+ if ($@ && $@ !~ /^Can't locate/) {
+ die $@;
+ };
+
+
+
+
+=head1 SEE ALSO
+
+This class allows "overlay" methods to be placed
+into the following files _Overlay is for a System overlay by the original author,
+while _Local is for site-local customizations.
+
+These overlay files can contain new subs or subs to replace existing subs in this module.
+
+If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line
+
+ no warnings qw(redefine);
+
+so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
+
+RT::FM::TopicCollection_Overlay, RT::FM::TopicCollection_Local
+
+=cut
+
+
+1;
Added: rtfm/trunk/lib/RT/FM/TopicCollection_Overlay.pm
==============================================================================
--- (empty file)
+++ rtfm/trunk/lib/RT/FM/TopicCollection_Overlay.pm Mon Jul 12 18:31:21 2004
@@ -0,0 +1,67 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse at bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# 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.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+use strict;
+no warnings qw(redefine);
+
+# {{{ LimitToObject
+
+=head2 LimitToObject OBJ
+
+Find all Topics hung off of the given Object
+
+=cut
+
+sub LimitToObject {
+ my $self = shift;
+ my $object = shift;
+
+ $self->Limit(FIELD => 'ObjectId',
+ VALUE => $object->Id);
+ $self->Limit(FIELD => 'ObjectType',
+ VALUE => ref($object));
+}
+
+# }}}
+
+
+# {{{ LimitToKids
+
+=head2 LimitToKids TOPIC
+
+Find all Topics which are immediate children of Id TOPIC. Note this
+does not do the recursive query of their kids, etc.
+
+=cut
+
+sub LimitToKids {
+ my $self = shift;
+ my $topic = shift;
+
+ $self->Limit(FIELD => 'Parent',
+ VALUE => $topic);
+}
+
+# }}}
+
+1;
Added: rtfm/trunk/lib/RT/FM/Topic_Overlay.pm
==============================================================================
--- (empty file)
+++ rtfm/trunk/lib/RT/FM/Topic_Overlay.pm Mon Jul 12 18:31:21 2004
@@ -0,0 +1,189 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse at bestpractical.com>
+#
+# (Except where explictly superceded by other copyright notices)
+#
+# 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.
+#
+# Unless otherwise specified, all modifications, corrections or
+# extensions to this work which alter its source code become the
+# property of Best Practical Solutions, LLC when submitted for
+# inclusion in the work.
+#
+#
+# END LICENSE BLOCK
+use strict;
+no warnings qw(redefine);
+
+# {{{ Create
+
+=head2 Create PARAMHASH
+
+Create takes a hash of values and creates a row in the database:
+
+ int(11) 'Parent'.
+ varchar(255) 'Name'.
+ varchar(255) 'Description'.
+ varchar(64) 'ObjectType'.
+ int(11) 'ObjectId'.
+
+=cut
+
+sub Create {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('AdminTopics') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ $self->SUPER::Create(@_);
+}
+
+# }}}
+
+
+# {{{ Delete
+
+=head2 Delete
+
+Deletes this topic, reparenting all sub-topics to this one's parent.
+
+=cut
+
+sub Delete {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('AdminTopics') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ my $kids = new RT::FM::TopicCollection($self->CurrentUser);
+ $kids->LimitToKids($self->Id);
+ while (my $topic = $kids->Next) {
+ $topic->setParent($self->Parent);
+ }
+
+ $self->SUPER::Delete(@_);
+ return (0, "Topic deleted");
+}
+
+# }}}
+
+
+# {{{ DeleteAll
+
+=head2 DeleteAll
+
+Deletes this topic, and all of its descendants.
+
+=cut
+
+sub DeleteAll {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('AdminTopics') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+ $self->SUPER::Delete(@_);
+ my $kids = new RT::FM::TopicCollection($self->CurrentUser);
+ $kids->LimitToKids($self->Id);
+ while (my $topic = $kids->Next) {
+ $topic->DeleteAll;
+ }
+
+ return (0, "Topic tree deleted");
+}
+
+# }}}
+
+
+# {{{ ParentObj
+
+=head2 ParentObj
+
+Returns the parent Topic of this one.
+
+=cut
+
+sub ParentObj {
+ my $self = shift;
+ my $id = $self->Parent;
+ my $obj = new RT::FM::Topic($self->CurrentUser);
+ $obj->Load($id);
+ return $obj;
+}
+
+# }}}
+
+
+# {{{ _Set
+
+=head2 _Set
+
+Intercept attempts to modify the Topic so we can apply ACLs
+
+=cut
+
+sub _Set {
+ my $self = shift;
+
+ unless ( $self->CurrentUserHasRight('AdminTopics') ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+ $self->SUPER::_Set(@_);
+}
+
+# }}}
+
+
+# {{{ CurrentUserHasRight
+
+=head2 CurrentUserHasRight
+
+Returns true if the current user has the right for this topic, for the
+whole system or for whatever object this topic is associated with
+
+=cut
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+
+ my $equiv = [ $RT::FM::System ];
+ if ($self->ObjectId) {
+ my $obj = $self->ObjectType->new($self->CurrentUser);
+ $obj->Load($self->ObjectId);
+ push @{$equiv}, $obj;
+ }
+ if ($self->Id) {
+ return ( $self->CurrentUser->HasRight(
+ Right => $right,
+ Object => $self,
+ EquivObjects => $equiv,
+ ) );
+ } else {
+ # If we don't have an ID, we don't even know what object we're
+ # attached to -- so the only thing we can fall back on is the
+ # system object.
+ return ( $self->CurrentUser->HasRight(
+ Right => $right,
+ Object => $RT::FM::System,
+ ) );
+ }
+
+
+}
+
+# }}}
+
+1;
More information about the Rt-commit
mailing list