summaryrefslogtreecommitdiff
path: root/rt/docs/design_docs
diff options
context:
space:
mode:
Diffstat (limited to 'rt/docs/design_docs')
-rw-r--r--rt/docs/design_docs/approval_notices8
-rw-r--r--rt/docs/design_docs/approval_template25
-rw-r--r--rt/docs/design_docs/cf_search72
-rw-r--r--rt/docs/design_docs/delegation115
-rw-r--r--rt/docs/design_docs/groups_notes88
-rw-r--r--rt/docs/design_docs/recursive_group_membership_algorithm109
-rw-r--r--rt/docs/design_docs/rql_parser_machine.graphviz32
-rw-r--r--rt/docs/design_docs/string-extraction-guide.txt100
-rw-r--r--rt/docs/design_docs/ticket_templates16
9 files changed, 565 insertions, 0 deletions
diff --git a/rt/docs/design_docs/approval_notices b/rt/docs/design_docs/approval_notices
new file mode 100644
index 000000000..5e7611924
--- /dev/null
+++ b/rt/docs/design_docs/approval_notices
@@ -0,0 +1,8 @@
+Notification on "your request approved by"
+Notification on "your request approved by all approvers"
+Notification on "your request denied by"
+Reject ticket on rejection of any approval
+
+"Ticket N is pending your approval"
+
+
diff --git a/rt/docs/design_docs/approval_template b/rt/docs/design_docs/approval_template
new file mode 100644
index 000000000..16a988c5d
--- /dev/null
+++ b/rt/docs/design_docs/approval_template
@@ -0,0 +1,25 @@
+===Create-Ticket: approval
+ { my $name = "HR";
+ my $groups = RT::Groups->new($RT::SystemUser);
+ $groups->LimitToUserDefinedGroups();
+ $groups->Limit(FIELD => 'Name', OPERATOR => '=', VALUE => "$name");
+ $groups->WithMember($TransactionObj->CreatorObj->Id);
+
+ my $groupid = $groups->First->Id;
+
+ my $adminccs = RT::Users->new($RT::SystemUser);
+ $adminccs->WhoHaveRight(Right => 'AdminGroup', IncludeSystemRights => undef, IncludeSuperusers => 0, IncludeSubgroupMembers => 0, Object => $groups->First);
+
+ my @admins;
+ while (my $admin = $adminccs->Next) {
+ push (@admins, $admin->Name);
+ }
+ }
+ Queue: Approvals
+ Type: Approval
+ AdminCcs: {join (", ",@admins) }
+ Depended-On-By: {$tickets{'TOP'}->Id}
+ Refers-To: {$tickets{'TOP'}->Id}
+ Due: {time + 86400}
+ Content-Type: text/plain
+ Content: Your approval is requested for the ticket {%$tickets{'TOP'}->Id}: {$tickets{'TOP'}->Subject}
diff --git a/rt/docs/design_docs/cf_search b/rt/docs/design_docs/cf_search
new file mode 100644
index 000000000..456a9febb
--- /dev/null
+++ b/rt/docs/design_docs/cf_search
@@ -0,0 +1,72 @@
+find all tickets where:
+
+
+ CF Foo
+ Has values (talk or read) AND
+ Has values (bar and baz) AND
+ doesn't have values (bing or bong)
+
+
+LimitCustomFieldValues {
+ my %args = ( CustomField => undef,
+ ClauseId => 'CustomFields',
+ OPERATOR => undef,
+ ENTRYAGGREGATOR => undef,
+ VALUES => undef,
+ @_) ;
+
+ unless ( $self->{'TicketAliases'}{$args{'ClauseId'}}{'CustomField'} ) {
+ $self->{'TicketAliases'}{$args{'ClauseId'}}{'CustomField'} = $self->NewAlias('CustomFields');
+ $self->Join(TABLE1 =>$self->{'TicketAliases'}{$args{'ClauseId'}}{'CustomField' },
+ FIELD1 => 'QueueId',
+ TABLE2 => 'main', FIELD2 => 'QueueId');
+
+ if ($args{'OPERATOR'} =~ /!=|IS/i) {
+ }
+ else {
+ }
+
+}
+ # {{{ if it's a keyword
+ elsif ( $TYPES{ $restriction->{'FIELD'} } eq 'CUSTOMFIELD' ) {
+
+ my $null_columns_ok;
+ my $TicketCFs = $self->Join( TYPE => 'left',
+ ALIAS1 => 'main',
+ FIELD1 => 'id',
+ TABLE2 => 'TicketCustomFieldValues',
+ FIELD2 => 'Ticket' );
+
+ foreach my $value ( @{ $restriction->{'VALUES'} } ) {
+ $self->SUPER::Limit( ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => $restriction->{'OPERATOR'},
+ VALUE => $value,
+ QUOTEVALUE => $restriction->{'QUOTEVALUE'},
+ ENTRYAGGREGATOR => 'AND', );
+ }
+ if ( ( $restriction->{'OPERATOR'} =~ /^IS$/i ) or ( $restriction->{'OPERATOR'} eq '!=' ) ) {
+ $null_columns_ok = 1;
+ }
+
+ #If we're trying to find tickets where the keyword isn't somethng, also check ones where it _IS_ null
+ if ( $restriction->{'OPERATOR'} eq '!=' ) {
+ $self->SUPER::Limit( ALIAS => $TicketCFs,
+ FIELD => 'Content',
+ OPERATOR => 'IS',
+ VALUE => 'NULL',
+ QUOTEVALUE => 0,
+ ENTRYAGGREGATOR => 'OR', );
+ }
+
+ $self->SUPER::Limit( LEFTJOIN => $TicketCFs,
+ FIELD => 'CustomField',
+ VALUE => $restriction->{'CUSTOMFIELD'},
+ ENTRYAGGREGATOR => 'OR' );
+
+ }
+
+ # }}}
+
+ }
+
diff --git a/rt/docs/design_docs/delegation b/rt/docs/design_docs/delegation
new file mode 100644
index 000000000..0e5705907
--- /dev/null
+++ b/rt/docs/design_docs/delegation
@@ -0,0 +1,115 @@
+Group ACLs
+
+ the rights:
+
+
+ CreatePersonalGroup
+ CreateGroup
+
+ AdminGroup
+ * Update group metadata and access control list
+ AdminGroupMembers
+ * Add ad delete members of this group
+ ModifyOwnMembership
+ * Join and quit this group
+
+
+ the primitives:
+
+In user.pm
+
+=item HasRight { Right => 'somerightname', ObjectType => 'Group', ObjectId => 'GroupId'
+
+ Returns true if this user has the right 'somerightname' for
+the group with id 'Id'
+
+=cut
+
+
+=item RightsForObject { ObjectType => 'Group', ObjectId =>'GroupId' }
+
+in users.pm
+
+=item WhoHaveRight { Right =>'somerightname', ObjectType => 'Group', ObjectId => 'GroupId' }
+
+
+ Finds all users who have the right 'somerightname' for the group
+in question.
+
+ If a user has "AdminGroupMembers" globally and we ask about
+ group 23, that user should be found.
+
+=cut
+
+Users must be able to delegate individual rights
+
+ * Is it that users can delegate any and all rights but it's
+ only rights they _have_ which actually grant rights.
+
+rights must not be redelegated
+
+users must be able to create groups to which rights can be delegated.
+
+Only users who have the "delegate rights" right can delegate rights.
+
+
+When a user's right to do something is revoked, the delegation must
+be revoked
+
+ * For any delegated ACL check, the delegator's right must be
+ checked immediately after the delegatee's right.
+ If a user has had a right delegated by multiple parties,
+ this may mean that we need to actually loop through and check
+ a bunch of possible delegations. Or can we craft a "has delegated
+ right" ACL check.
+
+
+
+
+
+
+
+ACL 1 Group Q has the right to Frob ObjectI.
+ACL 2 User A has the right "DelegateRights"
+
+Group Q has the member Group S
+Group S has the member Group R
+Group S has the member Group T
+Group R has the member user A
+Group T has the member user A
+
+User A delegates to Group P the right to Frob ObjectI
+
+ New ACL rule:
+
+ ACL 3: Group P has the right to Frob ObjectI
+ as delegated from ACL1 by User A
+
+
+In the case where ACL1 is revoked:
+
+ find all acls which are delegated from ACL1.
+ Delete them
+
+In the case where User A is removed from group R
+
+ Get the list of all groups that A was in by way of group R before the removal
+ Get the list of all groups that A is in _after_ the removal.
+
+ Find all the ACEs granted to each group that A is no longer in.
+ For each ACE in that list, find all the rights that A has delegated.
+ Whack them.
+
+In the case where Group S is removed from group Q
+
+
+ Get a list of all groups that S was in by way of Q before the removal
+ Call this list O.
+
+ For each user X who's a member of S (directly or indirectly):
+ Get a list of all groups that X is in after removal.
+ For each group in O that X is no longer a member of:
+ Find all ACEs granted to O
+ For each ACE, look up all the delegations that X has made.
+ For each delegation
+ WHACK IT
diff --git a/rt/docs/design_docs/groups_notes b/rt/docs/design_docs/groups_notes
new file mode 100644
index 000000000..234fd37fe
--- /dev/null
+++ b/rt/docs/design_docs/groups_notes
@@ -0,0 +1,88 @@
+CREATE TABLE Prinicpals (
+ id int auto_increment
+ PrincipalType VARCHAR(16) not_null,
+ PrincipalId int # foreign key to Users or Groups, depending
+)
+
+CREATE TABLE Groups (
+ id int auto_increment,
+ Domain varchar(255),
+ Instance varchar(16),
+ Name varchar(255),
+ Description varchar(255),
+);
+CREATE TABLE ACL (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Principal integer NULL , #Foreign key to principals
+ RightName varchar(25) NULL ,
+ RightDomain varchar(25) NULL ,
+ RightInstance integer NULL ,
+ PRIMARY KEY (id)
+);
+
+CREATE TABLE GroupMembers (
+ id int auto_increment,
+ Group int, # foreign key to Principals
+ Member int # foreign key to Principals
+)
+
+create table GroupMembersCache (
+ id int auto_increment,
+ Group int, # foreign key to Principals
+ Member int, # foreign key to Principals
+ Via int, #foreign key to g_m_u
+)
+
+insert into principals values ('bubbles);
+insert into principals values ('fubar');
+insert into principals values ('sheeri');
+insert into principals values ('sgw');
+
+insert into principals values ('staff');
+insert into principals values ('sysadmin');
+insert into principals values ('senior admin');
+
+
+insert into group_members values(1, 'staff', 'bubbles');
+insert into group_members values(2, 'sysadmin', 'sheeri');
+insert into group_members values(3,'senior admin', 'sgw');
+insert into group_members values(4,'senior admin', 'fubar');
+insert into group_members values(5, 'sysadmin', 'senior admin')
+
+Groups
+
+
+
+Domain Queues
+Instance <queueid#>
+Name AdminCc, Cc
+
+/Queues/1/AdminCc
+/Queues/3/Cc
+
+Domain Tickets
+Instance <#n>
+Name Owner, Requestor, Cc, AdminCc
+
+/Tickets/1/Owner
+/Tickets/1/Requestor
+/Tickets/1/Cc
+ Has members: /Queues/whatever queue the ticket has/Cc
+/Tickets/1/AdminCc
+ Has members: /Queues/whatever queue the ticket has/AdminCc
+
+
+Domain Users
+Instance <userid>
+
+/Users/1/MyDelegates
+/Users/1/MyOtherDelegates
+
+
+Domain System
+Name Admins, AdminManagers
+
+/System/Administrators
+/System/Blah
+
+
diff --git a/rt/docs/design_docs/recursive_group_membership_algorithm b/rt/docs/design_docs/recursive_group_membership_algorithm
new file mode 100644
index 000000000..250b9ad0d
--- /dev/null
+++ b/rt/docs/design_docs/recursive_group_membership_algorithm
@@ -0,0 +1,109 @@
+Group A has members 1, 2, 3
+
+ Cached members 1 is a member of A via ""
+ 2 is a member of A via ""
+ 3 is a member of A via ""
+
+
+Group B has members A, 4, 5
+
+ Cached members: 4 is a member of B via "" $1
+ 5 is a member of B via "" $2
+ A is a member of B via "" $3
+ 1 is a member of B via "$3" $4
+ 2 is a member of B via "$3" $5
+ 3 is a member of B via "$3" $6
+
+Group C has members A, B, 6
+ 6 is a member of C via "" $7
+ A is a member of C via "" $8
+ 1 is a member of C via $8 $9
+ 2 is a member of C via $8 $10
+ 3 is a member of C via $8 $11
+ B is a member of C via "" $12
+ 4 is a member of C via $12 $13
+ 5 is a member of C via $12 $14
+ A is a member of C via $12 $15
+ 1 is a member of C via $15 $16
+ 2 is a member of C via $15 $17
+ 3 is a member of C via $15 $18
+
+
+
+Group D has members A, C
+
+ A is a member of D via "" $19
+ 1 is a member of D via $19 $20
+ 2 is a member of D via $19 $21
+ 3 is a member of D via $19 $22
+ C is a member of D via "" $23
+ 6 is a member of D via $23 $24
+ A is a member of D via $23 $25
+ 1 is a member of D via $25 $26
+ 2 is a member of D via $25 $27
+ 3 is a member of D via $25 $28
+ B is a member of D via $23 $29
+ 4 is a member of D via $29 $30
+ 5 is a member of D via $29 $31
+ A is a member of D via $29 $32
+ 1 is a member of D via $32 $33
+ 2 is a member of D via $32 $34
+ 3 is a member of D via $32 $35
+
+
+
+Adding a new user, 7, to group A.
+
+
+ Add the user to group A in the groups table.
+
+ Find all entries for group A in the cache table.
+
+ For each entry in that list:
+ Add "7 is a member of $entry->top via $entry->id"
+
+Deleting a user, 7, from group A:
+
+ Remove the user from group A in the groups table.
+ find all entries in the cache table where the principal id is user 7 and
+ the parent id is A. (requires a self join)
+ nuke them
+
+ Alternatively:
+ find all entries for A in the cache table.
+ For each one, find the child whose id is 7.
+ Nuke it
+
+
+Adding a group, B to group D.
+
+ Add group B as a member of D in the groups table.
+ In the cache table:
+ $id = Add group B as a member of D via ""
+
+ For each member of group B (4, 5, A):
+
+ $sid= 4 is a member of D via $id
+ $sid= 5 is a member of D via $id
+ $sid= A is a member of D via $id
+
+ if the member is a group itself, recurse down:
+
+ 1 is a member of D via $sid
+ 2 is a member of D via $sid
+ 3 is a member of D via $sid
+
+ Find all places where D is a member of $foo.
+ Repeat the above procedure, substituting $foo for D
+ and making $id D's id.
+
+Removing B as a member of D:
+
+ Remove B as a member of D in the groups table.
+ Find all references to D in the pseudogroups table.
+ Find all children of D which are B:
+ Recurse down with the following algorithm:
+ If it's a user, delete it.
+ If it's a group, recurse through each member,
+ deleting its children and then deleting the
+ group itself.
diff --git a/rt/docs/design_docs/rql_parser_machine.graphviz b/rt/docs/design_docs/rql_parser_machine.graphviz
new file mode 100644
index 000000000..36463ecc9
--- /dev/null
+++ b/rt/docs/design_docs/rql_parser_machine.graphviz
@@ -0,0 +1,32 @@
+
+/* GraphViz graph representing the state diagram of the RQL parser.
+*/
+
+digraph G {
+
+ PAREN -> PAREN;
+ PAREN -> KEYWORD;
+ PAREN -> AGGREG;
+
+ AGGREG -> KEYWORD;
+ AGGREG -> PAREN;
+
+ KEYWORD -> OP;
+
+ OP -> VALUE;
+
+ VALUE -> PAREN;
+ VALUE -> AGGREG;
+
+/*
+ Blue lines represent added complexity of q[IN (x,y,z)] support.
+ The only place that the "blue tree" can be entered is at IN, and
+ exited at PAREN.
+*/
+ KEYWORD -> IN [color=blue];
+ IN -> PAREN [color=blue];
+ PAREN -> VALUE [color=blue];
+ VALUE -> COMMA [color=blue];
+ COMMA -> VALUE [color=blue];
+ VALUE -> PAREN [color=blue];
+}
diff --git a/rt/docs/design_docs/string-extraction-guide.txt b/rt/docs/design_docs/string-extraction-guide.txt
new file mode 100644
index 000000000..bd60a43fa
--- /dev/null
+++ b/rt/docs/design_docs/string-extraction-guide.txt
@@ -0,0 +1,100 @@
+# $File: //depot/RT/rt-devel/docs/design_docs/string-extraction-guide.txt $ $Author: ivan $
+# $Revision: 1.1 $ $Change: 1431 $ $DateTime: 2002/10/15 17:24:45 $
+
+Run 'p4 edit lib/RT/I18N/zh_tw.pm' and 'perl l10n.pl' to add new
+extractions to the zh_tw.pm.
+
+Edit lib/RT/I18N/zh_tw.pm for chinese counterparts.
+
+Attached is a copy of the freshly rewritten string extraction style guide.
+Please point out anything that's unclear or underspecified. I
+localized a number of the core modules in RT 2.1.3 (Starting with
+Queue_Overlay.pm). I only touched a couple of the web templates in the
+Elements/ directory of the web ui.
+
+RT String extraction styleguide:
+
+Web templates:
+
+Templates should use the /l filtering component to call the localisation
+framework
+
+The string Foo!
+
+Should become <&|/l&>Foo!</&>
+
+All newlines should be removed from localized strings, to make it easy to
+grep the codebase for strings to be localized
+
+The string Foo
+ Bar
+ Baz
+
+Should become <&|/l&>Foo Bar Baz</&>
+
+
+Variable subsititutions should be moved to Locale::MakeText format
+
+The string Hello, <%$name %>
+
+should become <&|/l, $name &>Hello, [_1]</&>
+
+
+Multiple variables work just like single variables
+
+The string You found <%$num%> tickets in queue <%$queue%>
+
+should become <&|/l, $num, $queue &>You found [_1] tickets in queue [_2]</&>
+
+When subcomponents are called in the middle of a phrase, they need to be escaped
+too:
+
+The string <input type="submit" value="New ticket in">&nbsp<& /Elements/SelectNewTicketQueue&>
+
+should become <&|/l, $m->scomp('/Elements/SelectNewTicketQueue')&><input type="submit" value="New ticket in">&nbsp;[_1]</&>
+
+
+
+There are places inside the web ui where strings are defined, which need to be
+localised. it is important to note here that each localized string is split out
+onto its own line, but never split across two lines and two localized strings
+are never included on the same line. It is also important to note
+that this will genereate code which will not work in RT 2.1.3. I need
+to add a bit of framework to make it work in 2.1.4
+
+
+The string <& /Elements/TitleBoxStart, width=> "40%", titleright => "RT $RT::VERSION for $RT::rtname", title => 'Login' &>
+
+should become <& /Elements/TitleBoxStart,
+ width=> "40%",
+ titleright => loc("RT [_1] for [_2]",$RT::VERSION, $RT::rtname),
+ title => loc('Login'),
+ &>
+
+
+
+
+
+
+Within RT's core code, every module has a localization handle available through the 'loc' method:
+
+The code return ( $id, "Queue created" );
+
+should become return ( $id, $self->loc("Queue created") );
+
+When returning or localizing a single string, the "extra" set of parenthesis () should be omitted.
+
+The code return ("Subject changed to ". $self->Data );
+
+should become return $self->loc( "Subject changed to [_1]", $self->Data );
+
+
+It is important not to localize the names of rights or statuses within RT's core, as there is logic that depends on them as string identifiers. The proper place to localize these values is when they're presented for display in the web or commandline interfaces.
+
+
+
+
+
+--
+http://www.bestpractical.com/products/rt -- Trouble Ticketing. Free.
+
diff --git a/rt/docs/design_docs/ticket_templates b/rt/docs/design_docs/ticket_templates
new file mode 100644
index 000000000..7850edf73
--- /dev/null
+++ b/rt/docs/design_docs/ticket_templates
@@ -0,0 +1,16 @@
+===Create-Ticket: foo
+ Subject: APPROVE <%TOP-Subject%>
+ Status: status
+ Queue: <%TOP-Queue%>
+ Owner: <%TOP-Owner%>
+ Depends-on: <%TOP-Id%>
+ Child-of: <%TOP-Id%>
+ Refers-to: <%TOP-Id%>
+ Content-Type: text/plain
+ Content: This is content
+blah
+blah
+blah
+===Create-Ticket: bar
+Subject: <%foo-Subject%>
+