summaryrefslogtreecommitdiff
path: root/rt
diff options
context:
space:
mode:
Diffstat (limited to 'rt')
-rw-r--r--rt/FREESIDE_MODIFIED48
-rw-r--r--rt/HOWTO/README14
-rw-r--r--rt/HOWTO/change.txt67
-rw-r--r--rt/HOWTO/release.txt124
-rw-r--r--rt/HOWTO/version-control.txt41
-rw-r--r--rt/Makefile399
-rwxr-xr-xrt/README509
-rwxr-xr-xrt/bin/mason_handler.fcgi15
-rwxr-xr-xrt/bin/mason_handler.scgi14
-rw-r--r--rt/bin/mason_handler.svc15
-rwxr-xr-xrt/bin/rt2589
-rw-r--r--rt/bin/rt-commit-handler2
-rw-r--r--rt/bin/rt-commit-handler.in846
-rw-r--r--rt/bin/rt-crontool213
-rwxr-xr-xrt/bin/rt-mailgate166
-rwxr-xr-xrt/bin/webmux.pl178
-rw-r--r--rt/config256
-rw-r--r--rt/config.layout.in (renamed from rt/config.layout)78
-rw-r--r--rt/config.log212
-rw-r--r--rt/config.pld20
-rwxr-xr-xrt/config.status1467
-rw-r--r--rt/etc/RT_Config.pm1728
-rw-r--r--rt/etc/RT_Config.pm.in1726
-rw-r--r--rt/etc/RT_SiteConfig.pm39
-rw-r--r--rt/etc/acl.Oracle12
-rwxr-xr-xrt/etc/acl.Pg109
-rwxr-xr-xrt/etc/acl.mysql29
-rw-r--r--rt/etc/schema.Oracle399
-rwxr-xr-xrt/etc/schema.Pg5
-rwxr-xr-xrt/etc/schema.mysql89
-rw-r--r--rt/etc/upgrade/2.1.71211
-rw-r--r--rt/html/Admin/Elements/ModifyQueue78
-rw-r--r--rt/html/Admin/Elements/ModifyUser99
-rw-r--r--rt/html/Admin/Global/CustomField.html86
-rw-r--r--rt/html/Admin/Global/CustomFields.html69
-rw-r--r--rt/html/Admin/Users/Modify.html12
-rw-r--r--rt/html/Admin/Users/Prefs.html122
-rw-r--r--rt/html/Callbacks/ActivityReports/Elements/Tabs/Default7
-rw-r--r--rt/html/Callbacks/ActivityReports/NoAuth/webrt.css/Default71
-rw-r--r--rt/html/Callbacks/ActivityReports/Search/Results.html/SearchActions7
-rw-r--r--rt/html/Callbacks/RT-WebCronTool/Elements/Tabs/Default13
-rw-r--r--rt/html/Callbacks/kStatistics/Elements/Tabs/Default11
-rw-r--r--rt/html/Developer/CronTool/autohandler9
-rw-r--r--rt/html/Developer/CronTool/index.html116
-rw-r--r--rt/html/Elements/AddCustomers59
-rw-r--r--rt/html/Elements/EditCustomers63
-rw-r--r--rt/html/Elements/Footer25
-rw-r--r--rt/html/Elements/FreesideInvoiceSearch20
-rw-r--r--rt/html/Elements/FreesideNewCust3
-rw-r--r--rt/html/Elements/FreesideSearch13
-rw-r--r--rt/html/Elements/FreesideSvcSearch13
-rw-r--r--rt/html/Elements/Header109
-rw-r--r--rt/html/Elements/PageLayout160
-rw-r--r--rt/html/Elements/QuickCreate4
-rw-r--r--rt/html/Elements/SelectDate15
-rw-r--r--rt/html/Elements/ShadedBox33
-rw-r--r--rt/html/Elements/ShadedInputRow35
-rw-r--r--rt/html/Elements/ShadedRow31
-rw-r--r--rt/html/Elements/SimpleSearch13
-rw-r--r--rt/html/Elements/Tabs22
-rw-r--r--rt/html/Elements/TicketList3
-rw-r--r--rt/html/Elements/ViewUser51
-rw-r--r--rt/html/NoAuth/css/3.5-default/freeside.css9
-rw-r--r--rt/html/NoAuth/css/3.5-default/main.css1
-rwxr-xr-xrt/html/NoAuth/css/3.5-default/misc.css8
-rw-r--r--rt/html/NoAuth/css/3.5-default/titlebox.css3
-rwxr-xr-xrt/html/NoAuth/css/3.5-default/transactions.css4
-rw-r--r--rt/html/NoAuth/images/back_home.gifbin330 -> 0 bytes
-rw-r--r--rt/html/NoAuth/images/css/cb.gifbin163 -> 110 bytes
-rw-r--r--rt/html/NoAuth/images/css/cbr.gifbin188 -> 110 bytes
-rw-r--r--rt/html/NoAuth/images/css/ct.gifbin162 -> 110 bytes
-rw-r--r--rt/html/NoAuth/images/css/ctr.gifbin188 -> 111 bytes
-rw-r--r--rt/html/NoAuth/images/head_requestracker.gifbin1233 -> 0 bytes
-rw-r--r--rt/html/NoAuth/images/rt.jpgbin917 -> 0 bytes
-rw-r--r--rt/html/NoAuth/images/small-logo.pngbin0 -> 4887 bytes
-rw-r--r--rt/html/NoAuth/images/space.gifbin43 -> 0 bytes
-rw-r--r--rt/html/NoAuth/images/spacer.gifbin43 -> 0 bytes
-rw-r--r--rt/html/NoAuth/images/squares_blue.gifbin219 -> 0 bytes
-rw-r--r--rt/html/NoAuth/printrt.css77
-rw-r--r--rt/html/NoAuth/webrt.css628
-rw-r--r--rt/html/Prefs/SearchOptions.html2
-rwxr-xr-xrt/html/RTx/Statistics/CallsMultiQueue/Elements/Chart39
-rwxr-xr-xrt/html/RTx/Statistics/CallsMultiQueue/index.html330
-rwxr-xr-xrt/html/RTx/Statistics/CallsQueueDay/Elements/Chart29
-rw-r--r--rt/html/RTx/Statistics/CallsQueueDay/Results.tsv191
-rwxr-xr-xrt/html/RTx/Statistics/CallsQueueDay/index.html275
-rwxr-xr-xrt/html/RTx/Statistics/DayOfWeek/Elements/Chart26
-rwxr-xr-xrt/html/RTx/Statistics/DayOfWeek/index.html155
-rwxr-xr-xrt/html/RTx/Statistics/DurationAsString18
-rw-r--r--rt/html/RTx/Statistics/Elements/CollectionAsTable/Header126
-rw-r--r--rt/html/RTx/Statistics/Elements/CollectionAsTable/ParseFormat (renamed from rt/html/Ticket/Elements/ShowReferences)83
-rw-r--r--rt/html/RTx/Statistics/Elements/CollectionAsTable/Row112
-rw-r--r--rt/html/RTx/Statistics/Elements/ControlsAsTable/ControlBox103
-rw-r--r--rt/html/RTx/Statistics/Elements/ControlsAsTable/UpdatePage5
-rw-r--r--rt/html/RTx/Statistics/Elements/DateSelectRow55
-rwxr-xr-xrt/html/RTx/Statistics/Elements/DurationAsString18
-rw-r--r--rt/html/RTx/Statistics/Elements/GraphBox27
-rwxr-xr-x[-rw-r--r--]rt/html/RTx/Statistics/Elements/SelectMultiQueue (renamed from rt/html/Ticket/Elements/ShowMemberOf)40
-rw-r--r--rt/html/RTx/Statistics/Elements/StatColumnMap173
-rwxr-xr-xrt/html/RTx/Statistics/Elements/Tabs72
-rw-r--r--rt/html/RTx/Statistics/FAQ/index.html23
-rwxr-xr-xrt/html/RTx/Statistics/OpenStalled/Elements/Chart27
-rw-r--r--rt/html/RTx/Statistics/OpenStalled/Results.tsv114
-rwxr-xr-xrt/html/RTx/Statistics/OpenStalled/index.html188
-rwxr-xr-xrt/html/RTx/Statistics/Resolution/Elements/Chart29
-rw-r--r--rt/html/RTx/Statistics/Resolution/index.html269
-rwxr-xr-xrt/html/RTx/Statistics/TimeToResolve/Elements/Chart23
-rwxr-xr-xrt/html/RTx/Statistics/TimeToResolve/index.html75
-rwxr-xr-xrt/html/RTx/Statistics/UserTest/Elements/Chart28
-rwxr-xr-xrt/html/RTx/Statistics/UserTest/index.html54
-rwxr-xr-xrt/html/RTx/Statistics/index.html59
-rw-r--r--rt/html/Reports/Activity/ActivityDetail.html83
-rw-r--r--rt/html/Reports/Activity/ActivitySummary.html61
-rw-r--r--rt/html/Reports/Activity/Elements/LimitReport23
-rw-r--r--rt/html/Reports/Activity/Elements/MiniPlot57
-rw-r--r--rt/html/Reports/Activity/Elements/PrintFooter7
-rw-r--r--rt/html/Reports/Activity/Elements/PrintHeader32
-rw-r--r--rt/html/Reports/Activity/Elements/ScreenFooter13
-rw-r--r--rt/html/Reports/Activity/Elements/ScreenHeader8
-rw-r--r--rt/html/Reports/Activity/Elements/Tabs52
-rw-r--r--rt/html/Reports/Activity/Elements/Wrapper16
-rw-r--r--rt/html/Reports/Activity/ResolutionComments.html62
-rw-r--r--rt/html/Reports/Activity/ResolutionStatistics.html95
-rw-r--r--rt/html/Reports/Activity/index.html29
-rw-r--r--rt/html/Search/Elements/PickRestriction142
-rw-r--r--rt/html/Search/Elements/TicketHeader40
-rw-r--r--rt/html/Search/Elements/TicketHeaderCell55
-rw-r--r--rt/html/Search/Elements/TicketRow55
-rw-r--r--rt/html/Search/Listing.html113
-rw-r--r--rt/html/Ticket/Create.html4
-rw-r--r--rt/html/Ticket/Display.html4
-rw-r--r--rt/html/Ticket/Elements/AddCustomers52
-rw-r--r--rt/html/Ticket/Elements/EditCustomers63
-rw-r--r--rt/html/Ticket/Elements/EditLinks133
-rw-r--r--rt/html/Ticket/Elements/ShowCustomers38
-rw-r--r--rt/html/Ticket/Elements/ShowLink40
-rw-r--r--rt/html/Ticket/Elements/ShowLinks87
-rw-r--r--rt/html/Ticket/Elements/ShowSummary6
-rw-r--r--rt/html/Ticket/Elements/ShowTransactionAttachments10
-rw-r--r--rt/html/Ticket/Elements/Tabs2
-rw-r--r--rt/html/Ticket/ModifyCustomers.html49
-rw-r--r--rt/html/User/Prefs.html2
-rw-r--r--rt/lib/RT.pm619
-rwxr-xr-xrt/lib/RT/ACE.pm99
-rwxr-xr-xrt/lib/RT/ACL.pm50
-rwxr-xr-xrt/lib/RT/Action/Autoreply.pm107
-rwxr-xr-xrt/lib/RT/Action/Generic.pm201
-rwxr-xr-xrt/lib/RT/Action/Notify.pm125
-rwxr-xr-xrt/lib/RT/Action/NotifyAsComment.pm46
-rw-r--r--rt/lib/RT/Action/ResolveMembers.pm45
-rwxr-xr-xrt/lib/RT/Action/SendEmail.pm1222
-rwxr-xr-xrt/lib/RT/Attachment.pm113
-rwxr-xr-xrt/lib/RT/Attachments.pm45
-rw-r--r--rt/lib/RT/Condition/AnyTransaction.pm45
-rwxr-xr-xrt/lib/RT/Condition/Generic.pm217
-rw-r--r--rt/lib/RT/Condition/StatusChange.pm47
-rwxr-xr-xrt/lib/RT/CurrentUser.pm425
-rw-r--r--rt/lib/RT/Date.pm1069
-rw-r--r--rt/lib/RT/Extension/ActivityReports.pm3
-rwxr-xr-xrt/lib/RT/Group.pm87
-rwxr-xr-xrt/lib/RT/GroupMember.pm63
-rwxr-xr-xrt/lib/RT/GroupMembers.pm45
-rwxr-xr-xrt/lib/RT/Groups.pm47
-rw-r--r--rt/lib/RT/Groups_Overlay.pm166
-rw-r--r--rt/lib/RT/Handle.pm1047
-rw-r--r--rt/lib/RT/I18N/en_malkovich.po3973
-rw-r--r--rt/lib/RT/Interface/CLI.pm60
-rwxr-xr-xrt/lib/RT/Interface/Email.pm1924
-rw-r--r--rt/lib/RT/Interface/Web.pm2250
-rw-r--r--rt/lib/RT/Interface/Web_Vendor.pm201
-rw-r--r--rt/lib/RT/Link.pm97
-rw-r--r--rt/lib/RT/Links.pm45
-rwxr-xr-xrt/lib/RT/Queue.pm115
-rwxr-xr-xrt/lib/RT/Queues.pm45
-rwxr-xr-xrt/lib/RT/Record.pm576
-rwxr-xr-xrt/lib/RT/Scrip.pm141
-rwxr-xr-xrt/lib/RT/ScripAction.pm91
-rwxr-xr-xrt/lib/RT/ScripActions.pm45
-rwxr-xr-xrt/lib/RT/ScripCondition.pm97
-rwxr-xr-xrt/lib/RT/ScripConditions.pm45
-rwxr-xr-xrt/lib/RT/Scrips.pm45
-rw-r--r--rt/lib/RT/SearchBuilder.pm96
-rwxr-xr-xrt/lib/RT/Template.pm111
-rwxr-xr-xrt/lib/RT/Templates.pm45
-rwxr-xr-xrt/lib/RT/Ticket.pm201
-rw-r--r--rt/lib/RT/TicketCustomFieldValue.pm308
-rw-r--r--rt/lib/RT/TicketCustomFieldValue_Overlay.pm74
-rw-r--r--rt/lib/RT/TicketCustomFieldValues.pm137
-rw-r--r--rt/lib/RT/TicketCustomFieldValues_Overlay.pm108
-rw-r--r--rt/lib/RT/Ticket_Overlay.pm1503
-rwxr-xr-xrt/lib/RT/Tickets.pm45
-rwxr-xr-xrt/lib/RT/Transaction.pm229
-rw-r--r--rt/lib/RT/Transaction_Overlay.pm487
-rwxr-xr-xrt/lib/RT/Transactions.pm45
-rw-r--r--rt/lib/RT/URI/freeside.pm285
-rw-r--r--rt/lib/RT/URI/freeside/Internal.pm145
-rw-r--r--rt/lib/RT/URI/freeside/XMLRPC.pm122
-rwxr-xr-xrt/lib/RT/User.pm241
-rw-r--r--rt/lib/RT/User_Overlay.pm1311
-rwxr-xr-xrt/lib/RT/Users.pm45
-rw-r--r--rt/lib/RT/Users_Overlay.pm85
-rwxr-xr-xrt/lib/RTx/Statistics.pm239
-rw-r--r--rt/lib/RTx/WebCronTool.pm41
-rw-r--r--rt/lib/t/00smoke.t.in14
-rw-r--r--rt/lib/t/01harness.t.in12
-rw-r--r--rt/lib/t/02regression.t7
-rw-r--r--rt/lib/t/02regression.t.in47
-rw-r--r--rt/lib/t/03web.pl78
-rw-r--r--rt/lib/t/03web.pl.in170
-rw-r--r--rt/lib/t/04_send_email.pl25
-rw-r--r--rt/lib/t/04_send_email.pl.in506
-rw-r--r--rt/lib/t/05cronsupport.pl.in84
-rw-r--r--rt/lib/t/regression/00placeholder1
-rw-r--r--rt/sbin/rt-setup-database474
-rw-r--r--rt/sbin/rt-setup-database.in866
-rw-r--r--rt/sbin/rt-test-dependencies574
216 files changed, 14199 insertions, 28168 deletions
diff --git a/rt/FREESIDE_MODIFIED b/rt/FREESIDE_MODIFIED
new file mode 100644
index 000000000..a4d552cac
--- /dev/null
+++ b/rt/FREESIDE_MODIFIED
@@ -0,0 +1,48 @@
+ sbin/rt-setup-database.in
+config.layout
+config.layout.in
+ etc/RT_SiteConfig.pm
+lib/RT/Interface/Web_Vendor.pm
+ lib/RT/Record.pm
+lib/RT/SearchBuilder.pm #need DBIx::SearchBuilder >= 1.36 for Pg 8.1+
+lib/RT/Transaction_Overlay.pm
+ lib/RT/Ticket_Overlay.pm
+ lib/RT/Users_Overlay.pm
+ lib/RT/Groups_Overlay.pm
+lib/RT/URI/freeside.pm
+lib/RT/URI/freeside/Internal.pm
+lib/RT/URI/freeside/XMLRPC.pm
+ html/Admin/Users/Modify.html
+ html/Elements/Header
+ html/Elements/Menu
+ html/Elements/PageLayout
+ html/Elements/QuickCreate
+ html/Elements/SelectDate
+ html/Elements/SimpleSearch
+ html/Elements/Tabs
+ html/Elements/Footer
+ html/Elements/CollectionAsTable/Row #backport from 3.3-TESTING
+ html/Ticket/Create.html
+ html/Ticket/Display.html
+html/Ticket/Elements/AddCustomers
+html/Ticket/Elements/EditCustomers
+html/Ticket/Elements/ShowCustomers
+ html/Ticket/Elements/ShowSummary
+ html/Ticket/Elements/Tabs
+html/Ticket/ModifyCustomers.html
+html/NoAuth/images/small-logo.png
+ html/NoAuth/css/3.5-default/main.css
+ html/NoAuth/css/3.5-default/misc.css
+ html/NoAuth/css/3.5-default/titlebox.css
+html/NoAuth/css/3.5-default/freeside.css
+
+html/Widgets/TitleBoxStart
+
+html/Elements/FreesideNewCust
+html/Elements/FreesideSearch
+html/Elements/FreesideSvcSearch
+html/Elements/AddCustomers
+html/Elements/EditCustomers
+
+ html/User/Prefs.html
+ html/Prefs/SearchOptions.html
diff --git a/rt/HOWTO/README b/rt/HOWTO/README
deleted file mode 100644
index 942096b0a..000000000
--- a/rt/HOWTO/README
+++ /dev/null
@@ -1,14 +0,0 @@
-Here you'll find plain text documentation of how to handle various
-project procedures. Files contained herein:
-
-change.txt
- How changes are integrated, including generating and
- distributing aedist change sets, and updating the CVS repository.
-
-release.txt
- Steps to go through when releasing a new version of RT.
-
-
-These procedures are based on documentation from the scons project
-as http://www.scons.org/
-
diff --git a/rt/HOWTO/change.txt b/rt/HOWTO/change.txt
deleted file mode 100644
index de316450c..000000000
--- a/rt/HOWTO/change.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-Handling a change set:
-
- -- Start the change:
-
- aedist -r [if it's a remote submission]
-
- -or-
-
- aedb {cnum} [if it's initiated locally]
-
- -- Normal development cycle:
-
- aecd -c {cnum}
- aecp . # Copy the baseline to your working dir
- # work on your change
- aenf {new file names}
-
- aecpu -unch # Remove unchanged files, for faster diffs
- aeb # Currently does nothing
- aet # Currently does nothing
- aed # Diff your change
- aede # End the change
-
- -- As the reviewer:
-
- aerpass {cnum}
-
- -- As the integrator:
-
- aeib {cnum}
- aeb
- aet
- aed
- cd ~ # Get out of the current working directory
- aeipass
-
-
-
-
- -- Update the aedist baseline on the web site:
-
- aedist -s -bl -p rt.2.1 > rt.2.1.ae
- scp rt.2.1.ae jesse@fsck.com:/home/ftp/pub/rt/devel/rt.2.1.ae
- rm rt.2.1.ae
-
- [This will eventually be automated.]
-
- -- Distribute the change to CVS:
-
- WARNING. DOES NOT YET WORK
-
- export CVS_RSH=ssh
- ae2cvs -n -aegis -p rt.2.1 -c {cnum} -u ~/SCons/scons
- ae2cvs -X -aegis -p rt.2.1 -c {cnum} -u ~/SCons/scons
-
- If you need the "ae2cvs" Perl script, you can find a copy
- checked in to the bin/subdirectory.
-
- [This may eventually be automated.]
-
-
-
- -- Grabbing the latest dev sources over ssh
-
- ssh fsck.com "aedist -s -p rt.2.1 -naa -bl -entire-source" | aedist -r
-
-
diff --git a/rt/HOWTO/release.txt b/rt/HOWTO/release.txt
deleted file mode 100644
index 285041c5b..000000000
--- a/rt/HOWTO/release.txt
+++ /dev/null
@@ -1,124 +0,0 @@
-Things to do to release a new version of rt:
-
- Build and test candidate packages
-
- Read through the README and src/README.txt files for any updates
-
- Prepare ChangeLog
-
- date -R the latest release
-
- should be current if this has been updated as each
- change went in.
-
- [ Should be automated ]
-
-
- TODO: nothing below this line is accurate for RT
-
- END THE BRANCH
-
- ae_p rt.2
- aede {5}
- aerpass {5}
- aeib {5}
- aeb
- aet
- aet -reg
- aed
- aeipass
-
- START THE NEW BRANCH
-
- aenbr -p rt.2 {6}
- aenc -p rt.2.{6}
-
- Call it something like, "Initialize the new
- branch." Cause = internal_enhancement. Exempt
- it from all tests (*_exempt = true).
-
- ae_p rt.2.{6}
-
- aedb 100
-
- aecd
-
- # Change the hard-coded package version numbers
- # in the following files.
- aecp rttruct debian/changelog rpm/rt.spec
-
- vi rttruct debian/changelog rpm/rt.spec
-
- # Optionally, do the same in the following:
- [optional] aecp HOWTO/change.txt
- [optional] aecp HOWTO/release.txt
- [optional] aecp debian/rt.postinst
-
- [optional] vi HOWTO/change.txt
- [optional] vi HOWTO/release.txt
- [optional] vi debian/rt.postinst
-
- aeb
-
- aet -reg
-
- aed
-
- aede
-
- etc.
-
-
- Read through the FAQ for any updates
-
- Test downloading from the web site download page
-
-
- In the Bugs Tracker, add a Group for the new release (0.05)
-
- Announce to the following mailing lists (template below):
-
- rt-announce@lists.fsck.com
-
-
- Notify www.cmtoday.com/contribute.html
-
- [This guy wants an announcement no more frequently than
- once a month, so save it for a future release if it's
- been too soon since the previous one.]
-
- Notify freshmeat.net
-
- [Wait until the morning so the announcement hits the
- main freshmeat.net page while people in the U.S. are
- awake and working]
-
-
-
-
-=======================
-
-Template release announcement:
-
-
-
-Version 2.1.XXX of rt has been released and is available for download
-from the rt web site:
-
- http://bestpractical.com/rt/
-
-
-
-WHAT'S NEW IN THIS RELEASE?
-
-Version 2.1.XXX of rt contains the following important changes:
-
- - XXX
-
-For a complete list of changes in version 2.1.XXX, see the CHANGES.txt
-file in the release itself.
-
-
-WHAT IS RT?
-
- FILL THIS IN
diff --git a/rt/HOWTO/version-control.txt b/rt/HOWTO/version-control.txt
deleted file mode 100644
index 06babfdf1..000000000
--- a/rt/HOWTO/version-control.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-Using Aegis for RT development
-
- 1. The main line of RT development will be under the control
- of the Aegis change management system, as administered by
- Best Practical Solutions, LLC
-
- 2. We will use aedist to generate change sets for each change
- checked in to the main Aegis repository. These change sets will be
- either distributed by a mailing list or made available via the web,
- or both.
-
- 3. Remote developers using Aegis will send aedist output for
- their changes to rt-patches@bestpractical.com for review and
- integration.
-
- 4. The aedist output should be sent to rt-patches@bestpractical.com
- after the change has completed its local aede, but before aerpass.
-
- 5. If the change is rejected, the developer can aedeu to reopen
- the change and fix whatever problems caused the review to not pass.
-
- 6. A baseline snapshot (aedist -bl) of the main Aegis repository
- will be generated at least daily and made available via http
- to provide a central location for synchronizing remote Aegis
- repositories.
-
- 7. Changes to the main Aegis repository will also be propagated
- automatically to the tracking CVS repository.
-
-Using CVS for RT development
-
- 1. CVS is accessed via anonymous cvs with the following CVSROOT:
-
- :pserver:anoncvs@cvs.fsck.com:/raid/cvsroot/rt-2-1
-
- 2. Remote developers using CVS will send patches (cvs -diff
- output) to rt-patches@bestpractical.com for integration into the
- main Aegis repository. This allows anonymous CVS access to be used
- for RT development by developers who are unable to use Aegis.
-
-
diff --git a/rt/Makefile b/rt/Makefile
index e89a69ac5..580721231 100644
--- a/rt/Makefile
+++ b/rt/Makefile
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -51,26 +51,23 @@
#
-PERL = /usr/bin/perl
-INSTALL = install-sh
-
-RT_LAYOUT = relative
+PERL = /usr/bin/perl
CONFIG_FILE_PATH = /opt/rt3/etc
-CONFIG_FILE = $(CONFIG_FILE_PATH)/RT_Config.pm
-SITE_CONFIG_FILE = $(CONFIG_FILE_PATH)/RT_SiteConfig.pm
+CONFIG_FILE = $(CONFIG_FILE_PATH)/RT_Config.pm
+SITE_CONFIG_FILE = $(CONFIG_FILE_PATH)/RT_SiteConfig.pm
RT_VERSION_MAJOR = 3
-RT_VERSION_MINOR = 8
-RT_VERSION_PATCH = 7
+RT_VERSION_MINOR = 6
+RT_VERSION_PATCH = 10
-RT_VERSION = $(RT_VERSION_MAJOR).$(RT_VERSION_MINOR).$(RT_VERSION_PATCH)
-TAG = rt-$(RT_VERSION_MAJOR)-$(RT_VERSION_MINOR)-$(RT_VERSION_PATCH)
+RT_VERSION = $(RT_VERSION_MAJOR).$(RT_VERSION_MINOR).$(RT_VERSION_PATCH)
+TAG = rt-$(RT_VERSION_MAJOR)-$(RT_VERSION_MINOR)-$(RT_VERSION_PATCH)
# This is the group that all of the installed files will be chgrp'ed to.
-RTGROUP = www
+RTGROUP = freeside
# User which should own rt binaries.
@@ -82,22 +79,20 @@ LIBS_OWNER = root
# Group that should own all of RT's libraries, generally root.
LIBS_GROUP = bin
-WEB_USER = www
-WEB_GROUP = www
+WEB_USER = freeside
+WEB_GROUP = freeside
-APACHECTL = /usr/sbin/apachectl
+APACHECTL =
# {{{ Files and directories
# DESTDIR allows you to specify that RT be installed somewhere other than
-# where it will eventually reside. DESTDIR _must_ have a trailing slash
-# if it's defined.
+# where it will eventually reside
DESTDIR =
-
RT_PATH = /opt/rt3
RT_ETC_PATH = /opt/rt3/etc
RT_BIN_PATH = /opt/rt3/bin
@@ -107,15 +102,14 @@ RT_MAN_PATH = /opt/rt3/man
RT_VAR_PATH = /opt/rt3/var
RT_DOC_PATH = /opt/rt3/share/doc
RT_LOCAL_PATH = /opt/rt3/local
-LOCAL_PLUGIN_PATH = /opt/rt3/local/plugins
LOCAL_ETC_PATH = /opt/rt3/local/etc
LOCAL_LIB_PATH = /opt/rt3/local/lib
LOCAL_LEXICON_PATH = /opt/rt3/local/po
-MASON_HTML_PATH = /opt/rt3/share/html
+MASON_HTML_PATH = /var/www/freeside/rt
MASON_LOCAL_HTML_PATH = /opt/rt3/local/html
-MASON_DATA_PATH = /opt/rt3/var/mason_data
+MASON_DATA_PATH = /usr/local/etc/freeside/masondata
MASON_SESSION_PATH = /opt/rt3/var/session_data
-RT_LOG_PATH = /opt/rt3/var/log
+RT_LOG_PATH = /opt/rt3/var/log
# RT_READABLE_DIR_MODE is the mode of directories that are generally meant
# to be accessable
@@ -127,69 +121,34 @@ RT_READABLE_DIR_MODE = 0755
# {{{ all these define the places that RT's binaries should get installed
# RT_MODPERL_HANDLER is the mason handler script for mod_perl
-RT_MODPERL_HANDLER = webmux.pl
+RT_MODPERL_HANDLER = $(RT_BIN_PATH)/webmux.pl
# RT_STANDALONE_SERVER is a stand-alone HTTP server
-RT_STANDALONE_SERVER = standalone_httpd
+RT_STANDALONE_SERVER = $(RT_BIN_PATH)/standalone_httpd
# RT_SPEEDYCGI_HANDLER is the mason handler script for SpeedyCGI
-RT_SPEEDYCGI_HANDLER = mason_handler.scgi
+RT_SPEEDYCGI_HANDLER = $(RT_BIN_PATH)/mason_handler.scgi
# RT_FASTCGI_HANDLER is the mason handler script for FastCGI
-RT_FASTCGI_HANDLER = mason_handler.fcgi
+RT_FASTCGI_HANDLER = $(RT_BIN_PATH)/mason_handler.fcgi
# RT_WIN32_FASTCGI_HANDLER is the mason handler script for FastCGI
-RT_WIN32_FASTCGI_HANDLER = mason_handler.svc
+RT_WIN32_FASTCGI_HANDLER = $(RT_BIN_PATH)/mason_handler.svc
# RT's CLI
-RT_CLI_BIN = rt
+RT_CLI_BIN = $(RT_BIN_PATH)/rt
# RT's mail gateway
-RT_MAILGATE_BIN = rt-mailgate
+RT_MAILGATE_BIN = $(RT_BIN_PATH)/rt-mailgate
# RT's cron tool
-RT_CRON_BIN = rt-crontool
+RT_CRON_BIN = $(RT_BIN_PATH)/rt-crontool
# }}}
-BINARIES = $(RT_MODPERL_HANDLER) \
- $(RT_MAILGATE_BIN) \
- $(RT_CLI_BIN) \
- $(RT_CRON_BIN) \
- $(RT_STANDALONE_SERVER) \
- $(RT_SPEEDYCGI_HANDLER) \
- $(RT_FASTCGI_HANDLER) \
- $(RT_WIN32_FASTCGI_HANDLER)
-
-
-
-
-
-SYSTEM_BINARIES = rt-dump-database \
- rt-setup-database \
- rt-email-digest \
- rt-email-dashboards \
- rt-email-group-admin \
- rt-server \
- rt-test-dependencies \
- rt-clean-sessions \
- rt-shredder \
- rt-validator
-
-
-ETC_FILES = acl.Informix \
- acl.Pg \
- acl.Oracle \
- acl.mysql \
- acl.Sybase \
- schema.Informix \
- schema.Pg \
- schema.Oracle \
- schema.mysql-4.0 \
- schema.mysql-4.1 \
- schema.Sybase \
- schema.SQLite \
- initialdata
-
-# }}}
-
-# {{{ Web frontend
-
-WEB_HANDLER = standalone
+BINARIES = $(DESTDIR)/$(RT_MODPERL_HANDLER) \
+ $(DESTDIR)/$(RT_MAILGATE_BIN) \
+ $(DESTDIR)/$(RT_CLI_BIN) \
+ $(DESTDIR)/$(RT_CRON_BIN) \
+ $(DESTDIR)/$(RT_STANDALONE_SERVER) \
+ $(DESTDIR)/$(RT_SPEEDYCGI_HANDLER) \
+ $(DESTDIR)/$(RT_FASTCGI_HANDLER) \
+ $(DESTDIR)/$(RT_WIN32_FASTCGI_HANDLER)
+SYSTEM_BINARIES = $(DESTDIR)/$(RT_SBIN_PATH)/
# }}}
@@ -201,7 +160,7 @@ WEB_HANDLER = standalone
# "Pg" is known to work
# "Informix" is known to work
-DB_TYPE = SQLite
+DB_TYPE = Pg
# Set DBA to the name of a unix account with the proper permissions and
# environment to run your commandline SQL sbin
@@ -213,7 +172,7 @@ DB_TYPE = SQLite
# For Oracle, you want 'system'
# For Informix, you want 'informix'
-DB_DBA = root
+DB_DBA = freeside
DB_HOST = localhost
@@ -239,28 +198,25 @@ DB_RT_HOST = localhost
# set this to the name you want to give to the RT database in
# your database server. For Oracle, this should be the name of your sid
-DB_DATABASE = rt3
-DB_RT_USER = rt_user
-DB_RT_PASS = rt_pass
+DB_DATABASE = freeside
+DB_RT_USER = freeside
+DB_RT_PASS =
# }}}
-TEST_FILES = t/*.t t/*/*.t
-TEST_VERBOSE = 0
-
-
####################################################################
all: default
default:
- @echo "Please read RT's README before beginning your installation."
+ @echo "Please read RT's readme before installing. Not doing so could"
+ @echo "be dangerous."
instruct:
- @echo "Congratulations. RT is now installed."
+ @echo "Congratulations. RT has been installed. "
@echo ""
@echo ""
@echo "You must now configure RT by editing $(SITE_CONFIG_FILE)."
@@ -274,214 +230,244 @@ instruct:
@echo "After that, you need to initialize RT's database by running"
@echo " 'make initialize-database'"
+# @echo " or by executing "
+# @echo " '$(RT_SBIN_PATH)/rt-setup-database --action init \ "
+# @echo " --dba $(DB_DBA) --prompt-for-dba-password'"
+
+
upgrade-instruct:
- @echo "Congratulations. RT has been upgraded. You should now check over"
+ @echo "Congratulations. RT has been upgraded. You should now check-over"
@echo "$(CONFIG_FILE) for any necessary site customization. Additionally,"
@echo "you should update RT's system database objects by running "
- @echo " $(RT_SBIN_PATH)/rt-setup-database --dba $(DB_DBA) --prompt-for-dba-password --action upgrade"
+ @echo " ls etc/upgrade"
+ @echo ""
+ @echo "For each item in that directory whose name is greater than"
+ @echo "your previously installed RT version, run:"
+ @echo " $(RT_SBIN_PATH)/rt-setup-database --dba $(DB_DBA) --prompt-for-dba-password --action schema --datadir etc/upgrade/<version>"
+ @echo " $(RT_SBIN_PATH)/rt-setup-database --dba $(DB_DBA) --prompt-for-dba-password --action acl --datadir etc/upgrade/<version>"
+ @echo " $(RT_SBIN_PATH)/rt-setup-database --dba $(DB_DBA) --prompt-for-dba-password --action insert --datadir etc/upgrade/<version>"
-upgrade: testdeps config-install dirs files-install fixperms upgrade-instruct
+upgrade: config-install dirs files-install fixperms upgrade-instruct
-upgrade-noclobber: config-install dirs libs-install html-install bin-install local-install doc-install fixperms
+upgrade-noclobber: config-install libs-install html-install bin-install local-install doc-install fixperms
# {{{ dependencies
-
-my_with_web_handlers= $(shell $(PERL) -e 'print join " ", map "--with-$$_", grep defined && length, split /,/, "$(WEB_HANDLER)"')
testdeps:
- $(PERL) ./sbin/rt-test-dependencies --verbose --with-$(DB_TYPE) $(my_with_web_handlers)
+ $(PERL) ./sbin/rt-test-dependencies --verbose --with-$(DB_TYPE)
depends: fixdeps
fixdeps:
- $(PERL) ./sbin/rt-test-dependencies --verbose --install --with-$(DB_TYPE) $(my_with_web_handlers)
+ $(PERL) ./sbin/rt-test-dependencies --verbose --install --with-$(DB_TYPE)
#}}}
# {{{ fixperms
fixperms:
# Make the libraries readable
- chmod $(RT_READABLE_DIR_MODE) $(DESTDIR)$(RT_PATH)
- chown -R $(LIBS_OWNER) $(DESTDIR)$(RT_LIB_PATH)
- chgrp -R $(LIBS_GROUP) $(DESTDIR)$(RT_LIB_PATH)
- chmod -R u+rwX,go-w,go+rX $(DESTDIR)$(RT_LIB_PATH)
+ chmod $(RT_READABLE_DIR_MODE) $(DESTDIR)/$(RT_PATH)
+ chown -R $(LIBS_OWNER) $(DESTDIR)/$(RT_LIB_PATH)
+ chgrp -R $(LIBS_GROUP) $(DESTDIR)/$(RT_LIB_PATH)
+ chmod -R u+rwX,go-w,go+rX $(DESTDIR)/$(RT_LIB_PATH)
- chmod $(RT_READABLE_DIR_MODE) $(DESTDIR)$(RT_BIN_PATH)
+ chmod $(RT_READABLE_DIR_MODE) $(DESTDIR)/$(RT_BIN_PATH)
+ chmod $(RT_READABLE_DIR_MODE) $(DESTDIR)/$(RT_BIN_PATH)
- chmod 0755 $(DESTDIR)$(RT_ETC_PATH)
- cd $(DESTDIR)$(RT_ETC_PATH) && chmod 0400 $(ETC_FILES)
+ chmod 0755 $(DESTDIR)/$(RT_ETC_PATH)
+ chmod 0500 $(DESTDIR)/$(RT_ETC_PATH)/*
#TODO: the config file should probably be able to have its
# owner set separately from the binaries.
- chown -R $(BIN_OWNER) $(DESTDIR)$(RT_ETC_PATH)
- chgrp -R $(RTGROUP) $(DESTDIR)$(RT_ETC_PATH)
-
- chmod 0440 $(DESTDIR)$(CONFIG_FILE)
- chmod 0640 $(DESTDIR)$(SITE_CONFIG_FILE)
+ chown -R $(BIN_OWNER) $(DESTDIR)/$(RT_ETC_PATH)
+ chgrp -R $(RTGROUP) $(DESTDIR)/$(RT_ETC_PATH)
- # Make the system binaries
- cd $(DESTDIR)$(RT_BIN_PATH) && ( chmod 0755 $(BINARIES) ; chown $(BIN_OWNER) $(BINARIES); chgrp $(RTGROUP) $(BINARIES))
+ chmod 0550 $(DESTDIR)/$(CONFIG_FILE)
+ chmod 0550 $(DESTDIR)/$(SITE_CONFIG_FILE)
- # Make the system binaries executable also
- cd $(DESTDIR)$(RT_SBIN_PATH) && ( chmod 0755 $(SYSTEM_BINARIES) ; chown $(BIN_OWNER) $(SYSTEM_BINARIES); chgrp $(RTGROUP) $(SYSTEM_BINARIES))
+ # Make the interfaces executable
+ chown $(BIN_OWNER) $(BINARIES)
+ chgrp $(RTGROUP) $(BINARIES)
+ chmod 0755 $(BINARIES)
# Make the web ui readable by all.
- chmod -R u+rwX,go-w,go+rX $(DESTDIR)$(MASON_HTML_PATH) \
- $(DESTDIR)$(MASON_LOCAL_HTML_PATH) \
- $(DESTDIR)$(LOCAL_LEXICON_PATH)
- chown -R $(LIBS_OWNER) $(DESTDIR)$(MASON_HTML_PATH) \
- $(DESTDIR)$(MASON_LOCAL_HTML_PATH)
- chgrp -R $(LIBS_GROUP) $(DESTDIR)$(MASON_HTML_PATH) \
- $(DESTDIR)$(MASON_LOCAL_HTML_PATH)
+ chmod -R u+rwX,go-w,go+rX $(DESTDIR)/$(MASON_HTML_PATH) \
+ $(DESTDIR)/$(MASON_LOCAL_HTML_PATH) \
+ $(DESTDIR)/$(LOCAL_LEXICON_PATH)
+ chown -R $(LIBS_OWNER) $(DESTDIR)/$(MASON_HTML_PATH) \
+ $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
+ chgrp -R $(LIBS_GROUP) $(DESTDIR)/$(MASON_HTML_PATH) \
+ $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
# Make the web ui's data dir writable
- chmod 0770 $(DESTDIR)$(MASON_DATA_PATH) \
- $(DESTDIR)$(MASON_SESSION_PATH)
- chown -R $(WEB_USER) $(DESTDIR)$(MASON_DATA_PATH) \
- $(DESTDIR)$(MASON_SESSION_PATH)
- chgrp -R $(WEB_GROUP) $(DESTDIR)$(MASON_DATA_PATH) \
- $(DESTDIR)$(MASON_SESSION_PATH)
+ chmod 0770 $(DESTDIR)/$(MASON_DATA_PATH) \
+ $(DESTDIR)/$(MASON_SESSION_PATH)
+ chown -R $(WEB_USER) $(DESTDIR)/$(MASON_DATA_PATH) \
+ $(DESTDIR)/$(MASON_SESSION_PATH)
+ chgrp -R $(WEB_GROUP) $(DESTDIR)/$(MASON_DATA_PATH) \
+ $(DESTDIR)/$(MASON_SESSION_PATH)
# }}}
# {{{ dirs
dirs:
- $(INSTALL) -m 0755 -d $(DESTDIR)$(RT_LOG_PATH)
- $(INSTALL) -m 0770 -d $(DESTDIR)$(MASON_DATA_PATH)
- $(INSTALL) -m 0770 -d $(DESTDIR)$(MASON_DATA_PATH)/cache
- $(INSTALL) -m 0770 -d $(DESTDIR)$(MASON_DATA_PATH)/etc
- $(INSTALL) -m 0770 -d $(DESTDIR)$(MASON_DATA_PATH)/obj
- $(INSTALL) -m 0770 -d $(DESTDIR)$(MASON_SESSION_PATH)
- $(INSTALL) -m 0755 -d $(DESTDIR)$(MASON_HTML_PATH)
- $(INSTALL) -m 0755 -d $(DESTDIR)$(MASON_LOCAL_HTML_PATH)
- $(INSTALL) -m 0755 -d $(DESTDIR)$(LOCAL_ETC_PATH)
- $(INSTALL) -m 0755 -d $(DESTDIR)$(LOCAL_LIB_PATH)
- $(INSTALL) -m 0755 -d $(DESTDIR)$(LOCAL_PLUGIN_PATH)
- $(INSTALL) -m 0755 -d $(DESTDIR)$(LOCAL_LEXICON_PATH)
+ mkdir -p $(DESTDIR)/$(RT_LOG_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)/cache
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)/etc
+ mkdir -p $(DESTDIR)/$(MASON_DATA_PATH)/obj
+ mkdir -p $(DESTDIR)/$(MASON_SESSION_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_HTML_PATH)
+ mkdir -p $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
+ mkdir -p $(DESTDIR)/$(LOCAL_ETC_PATH)
+ mkdir -p $(DESTDIR)/$(LOCAL_LIB_PATH)
+ mkdir -p $(DESTDIR)/$(LOCAL_LEXICON_PATH)
# }}}
-install: testdeps config-install dirs files-install fixperms instruct
+install: config-install dirs files-install fixperms instruct
-files-install: libs-install etc-install config-install bin-install sbin-install html-install local-install doc-install
+files-install: libs-install etc-install bin-install sbin-install html-install local-install doc-install
config-install:
- $(INSTALL) -m 0755 -o $(BIN_OWNER) -g $(RTGROUP) -d $(DESTDIR)$(CONFIG_FILE_PATH)
- -$(INSTALL) -m 0440 -o $(BIN_OWNER) -g $(RTGROUP) etc/RT_Config.pm $(DESTDIR)$(CONFIG_FILE)
- [ -f $(DESTDIR)$(SITE_CONFIG_FILE) ] || $(INSTALL) -m 0640 -o $(BIN_OWNER) -g $(RTGROUP) etc/RT_SiteConfig.pm $(DESTDIR)$(SITE_CONFIG_FILE)
- @echo "Installed configuration. About to install RT in $(RT_PATH)"
+ mkdir -p $(DESTDIR)/$(CONFIG_FILE_PATH)
+ -cp etc/RT_Config.pm $(DESTDIR)/$(CONFIG_FILE)
+ [ -f $(DESTDIR)/$(SITE_CONFIG_FILE) ] || cp etc/RT_SiteConfig.pm $(DESTDIR)/$(SITE_CONFIG_FILE)
-test:
- $(PERL) "-MExtUtils::Command::MM" -e "test_harness($(TEST_VERBOSE), 'lib')" $(TEST_FILES)
+ chgrp $(RTGROUP) $(DESTDIR)/$(CONFIG_FILE)
+ chown $(BIN_OWNER) $(DESTDIR)/$(CONFIG_FILE)
+
+ chgrp $(RTGROUP) $(DESTDIR)/$(SITE_CONFIG_FILE)
+ chown $(BIN_OWNER) $(DESTDIR)/$(SITE_CONFIG_FILE)
-parallel-test: test-parallel
+ @echo "Installed configuration. about to install rt in $(RT_PATH)"
-test-parallel:
- RT_TEST_PARALLEL=1 HARNESS_OPTIONS="j4" $(PERL) "-MExtUtils::Command::MM" -e "test_harness($(TEST_VERBOSE), 'lib')" $(TEST_FILES)
+test:
+ $(PERL) -Ilib lib/t/00smoke.t
regression-install: config-install
- $(PERL) -pi -e 's/Set\(\$$DatabaseName.*\);/Set\(\$$DatabaseName, "rt3regression"\);/' $(DESTDIR)$(CONFIG_FILE)
+ $(PERL) -pi -e 's/Set\(\$$DatabaseName.*\);/Set\(\$$DatabaseName, "rt3regression"\);/' $(DESTDIR)/$(CONFIG_FILE)
+
+regression: regression-install dirs files-install libs-install sbin-install bin-install regression-instruct regression-reset-db testify-pods fixperms apachectl run-regression
+
+run-regression:
+ prove -Ilib lib/t/setup_regression.t lib/t/autogen/ lib/t/regression/
+
+
+regression-noapache: regression-install dirs files-install libs-install sbin-install bin-install regression-instruct regression-reset-db testify-pods fixperms start-httpd run-regression
+
+regression-quiet:
+ $(PERL) sbin/regression_harness
+
+regression-instruct:
+ @echo "About to wipe your database for a regression test. ABORT NOW with Control-C"
+
# {{{ database-installation
-regression-reset-db: force-dropdb
- $(PERL) -I$(LOCAL_LIB_PATH) -I$(RT_LIB_PATH) sbin/rt-setup-database --action init --dba $(DB_DBA) --dba-password ''
+regression-reset-db:
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action drop --dba $(DB_DBA) --dba-password '' --force
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action init --dba $(DB_DBA) --dba-password ''
initdb :: initialize-database
initialize-database:
- $(PERL) -I$(LOCAL_LIB_PATH) -I$(RT_LIB_PATH) sbin/rt-setup-database --action init --dba $(DB_DBA) --prompt-for-dba-password
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action init --dba $(DB_DBA) --prompt-for-dba-password
dropdb:
- $(PERL) -I$(LOCAL_LIB_PATH) -I$(RT_LIB_PATH) sbin/rt-setup-database --action drop --dba $(DB_DBA) --prompt-for-dba-password
-
-force-dropdb:
- $(PERL) -I$(LOCAL_LIB_PATH) -I$(RT_LIB_PATH) sbin/rt-setup-database --action drop --dba $(DB_DBA) --dba-password '' --force
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/rt-setup-database --action drop --dba $(DB_DBA) --prompt-for-dba-password
+insert-approval-data:
+ $(PERL) $(DESTDIR)/$(RT_SBIN_PATH)/insert_approval_scrips
# }}}
# {{{ libs-install
libs-install:
- [ -d $(DESTDIR)$(RT_LIB_PATH) ] || $(INSTALL) -m 0755 -d $(DESTDIR)$(RT_LIB_PATH)
- -( cd lib && find . -type d -print ) | while read dir ; do \
- $(INSTALL) -m 0755 -d "$(DESTDIR)$(RT_LIB_PATH)/$$dir" ; \
- done
- -( cd lib && find . -type f -print ) | while read file ; do \
- $(INSTALL) -m 0644 "lib/$$file" "$(DESTDIR)$(RT_LIB_PATH)/$$file" ; \
- done
+ [ -d $(DESTDIR)/$(RT_LIB_PATH) ] || mkdir -p $(DESTDIR)/$(RT_LIB_PATH)
+ -cp -rp lib/* $(DESTDIR)/$(RT_LIB_PATH)
# }}}
# {{{ html-install
html-install:
- [ -d $(DESTDIR)$(MASON_HTML_PATH) ] || $(INSTALL) -m 0755 -d $(DESTDIR)$(MASON_HTML_PATH)
- -( cd share/html && find . -type d -print ) | while read dir ; do \
- $(INSTALL) -m 0755 -d "$(DESTDIR)$(MASON_HTML_PATH)/$$dir" ; \
- done
- -( cd share/html && find . -type f -print ) | while read file ; do \
- $(INSTALL) -m 0644 "share/html/$$file" "$(DESTDIR)$(MASON_HTML_PATH)/$$file" ; \
- done
+ [ -d $(DESTDIR)/$(MASON_HTML_PATH) ] || mkdir -p $(DESTDIR)/$(MASON_HTML_PATH)
+ -cp -rp ./html/* $(DESTDIR)/$(MASON_HTML_PATH)
# }}}
# {{{ doc-install
doc-install:
# RT 3.0.0 - RT 3.0.2 would accidentally create a file instead of a dir
- -[ -f $(DESTDIR)$(RT_DOC_PATH) ] && rm $(DESTDIR)$(RT_DOC_PATH)
- [ -d $(DESTDIR)$(RT_DOC_PATH) ] || $(INSTALL) -m 0755 -d $(DESTDIR)$(RT_DOC_PATH)
- -$(INSTALL) -m 0644 ./README $(DESTDIR)$(RT_DOC_PATH)/
+ -[ -f $(DESTDIR)/$(RT_DOC_PATH) ] && rm $(DESTDIR)/$(RT_DOC_PATH)
+ [ -d $(DESTDIR)/$(RT_DOC_PATH) ] || mkdir -p $(DESTDIR)/$(RT_DOC_PATH)
+ -cp -rp ./README $(DESTDIR)/$(RT_DOC_PATH)
# }}}
# {{{ etc-install
etc-install:
- [ -d $(DESTDIR)$(RT_ETC_PATH) ] || $(INSTALL) -m 0755 -d $(DESTDIR)$(RT_ETC_PATH)
- for file in $(ETC_FILES) ; do \
- $(INSTALL) -m 0644 "etc/$$file" "$(DESTDIR)$(RT_ETC_PATH)/" ; \
- done
+ mkdir -p $(DESTDIR)/$(RT_ETC_PATH)
+ -cp -rp \
+ etc/acl.* \
+ etc/initialdata \
+ etc/schema.* \
+ $(DESTDIR)/$(RT_ETC_PATH)
# }}}
# {{{ sbin-install
sbin-install:
- $(INSTALL) -m 0755 -d $(DESTDIR)$(RT_SBIN_PATH)
- for file in $(SYSTEM_BINARIES) ; do \
- $(INSTALL) -o $(BIN_OWNER) -g $(RTGROUP) -m 0755 "sbin/$$file" "$(DESTDIR)$(RT_SBIN_PATH)/" ; \
- done
+ mkdir -p $(DESTDIR)/$(RT_SBIN_PATH)
+ chmod +x \
+ sbin/rt-dump-database \
+ sbin/rt-setup-database \
+ sbin/rt-test-dependencies
+ -cp -rp \
+ sbin/rt-dump-database \
+ sbin/rt-setup-database \
+ sbin/rt-test-dependencies \
+ $(DESTDIR)/$(RT_SBIN_PATH)
# }}}
# {{{ bin-install
bin-install:
- $(INSTALL) -m 0755 -d $(DESTDIR)$(RT_BIN_PATH)
- for file in $(BINARIES) ; do \
- $(INSTALL) -o $(BIN_OWNER) -g $(RTGROUP) -m 0755 "bin/$$file" "$(DESTDIR)$(RT_BIN_PATH)/" ; \
- done
+ mkdir -p $(DESTDIR)/$(RT_BIN_PATH)
+ chmod +x bin/rt-mailgate \
+ bin/rt-crontool
+ -cp -rp \
+ bin/rt-mailgate \
+ bin/mason_handler.fcgi \
+ bin/mason_handler.scgi \
+ bin/standalone_httpd \
+ bin/mason_handler.svc \
+ bin/rt \
+ bin/webmux.pl \
+ bin/rt-crontool \
+ $(DESTDIR)/$(RT_BIN_PATH)
+# }}}
# {{{ local-install
local-install:
- -( cd local/html && find . -type d -print ) | while read dir ; do \
- $(INSTALL) -m 0755 -d "$(DESTDIR)$(MASON_LOCAL_HTML_PATH)/$$dir" ; \
- done
- -( cd local/html && find . -type f -print ) | while read file ; do \
- $(INSTALL) -m 0644 "local/html/$$file" "$(DESTDIR)$(MASON_LOCAL_HTML_PATH)/$$file" ; \
- done
- -( cd local/po && find . -type d -print ) | while read dir ; do \
- $(INSTALL) -m 0755 -d "$(DESTDIR)$(LOCAL_LEXICON_PATH)/$$dir" ; \
- done
- -( cd local/po && find . -type f -print ) | while read file ; do \
- $(INSTALL) -m 0644 "local/po/$$file" "$(DESTDIR)$(LOCAL_LEXICON_PATH)/$$file" ; \
- done
- -( cd local/etc && find . -type d -print ) | while read dir ; do \
- $(INSTALL) -m 0755 -d "$(DESTDIR)$(LOCAL_ETC_PATH)/$$dir" ; \
- done
- -( cd local/etc && find . -type f -print ) | while read file ; do \
- $(INSTALL) -m 0644 "etc/$$file" "$(DESTDIR)$(LOCAL_ETC_PATH)/$$file" ; \
- done
+ -cp -rp ./local/html/* $(DESTDIR)/$(MASON_LOCAL_HTML_PATH)
+ -cp -rp ./local/po/* $(DESTDIR)/$(LOCAL_LEXICON_PATH)
+ -cp -rp ./local/etc/* $(DESTDIR)/$(LOCAL_ETC_PATH)
# }}}
# {{{ Best Practical Build targets -- no user servicable parts inside
+
+POD2TEST_EXE = sbin/extract_pod_tests
+
+testify-pods:
+ [ -d lib/t/autogen ] || mkdir lib/t/autogen
+ find lib -name \*pm |grep -v .svn | grep -v \*.in |xargs -n 1 $(PERL) $(POD2TEST_EXE)
+ find bin -type f |grep -v .svn | grep -v \~ | grep -v "\.in" | xargs -n 1 $(PERL) $(POD2TEST_EXE)
+ find lib -name \*pm |grep -v .svn | grep -v \*.in |xargs -n 1 $(PERL) $(POD2TEST_EXE)
+ find bin -type f |grep -v .svn | grep -v \~ | grep -v "\.in" | xargs -n 1 $(PERL) $(POD2TEST_EXE)
+
+
+
regenerate-catalogs:
$(PERL) sbin/extract-message-catalog
@@ -500,20 +486,9 @@ reconfigure:
start-httpd:
$(PERL) bin/standalone_httpd &
-start-server:
- $(PERL) sbin/rt-server &
-
apachectl:
$(APACHECTL) stop
sleep 10
$(APACHECTL) start
sleep 5
-
-SNAPSHOT=$(shell git describe --tags)
-snapshot:
- git archive --prefix "$(SNAPSHOT)/" HEAD | tar -xf -
- ( cd $(SNAPSHOT) && autoconf && ./configure )
- tar -czf "$(SNAPSHOT).tar.gz" "$(SNAPSHOT)/"
- rm -fr "$(SNAPSHOT)/"
-
# }}}
diff --git a/rt/README b/rt/README
index 1f78515a6..7c5e4d47a 100755
--- a/rt/README
+++ b/rt/README
@@ -1,369 +1,278 @@
-RT is an enterprise-grade issue tracking system. It allows organizations
-to keep track of what needs to get done, who is working on which tasks,
-what's already been done, and when tasks were (or weren't) completed.
-
-RT doesn't cost anything to use, no matter how much you use it; it
-is freely available under the terms of Version 2 of the GNU General
-Public License.
-
-RT is commercially-supported software. To purchase support, training,
-custom development, or professional services, please get in touch with
-us at sales@bestpractical.com.
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
+RT is an enterprise-grade issue tracking system. It allows
+organizations to keep track of their to-do lists, who is working
+on which tasks, what's already been done, and when tasks were
+completed. It is available under the terms of version 2 of the GNU
+General Public License (GPL), so it doesn't cost anything to set
+up and use.
- Jesse Vincent
- Best Practical Solutions, LLC
- March, 2005
+ Jesse Vincent
+ Best Practical Solutions, LLC
+ March 2003
-REQUIRED PACKAGES
------------------
+REQUIRED PACKAGES:
+------------------
-o Perl 5.8.3 or later (http://www.perl.org).
+o Perl 5.8.0 or later (http://www.perl.com).
- Perl versions prior to 5.8.3 contain bugs that could result
- in data corruption. RT won't start on older versions.
+ (If you intend to use the FastCGI or SpeedyCGI support, you
+ need to make sure that perl has been built with support for
+ setgid perl scripts.)`
-o A supported SQL database
+ Perl 5.6.1 is currently deprecated and will be officially desupported
+ in a future release
- Currently supported: Mysql 4.0.13 or later with InnoDB support.
+o A DB backend; MySQL is recommended ( http://www.mysql.com )
+ Currently supported: Mysql 4.0.13 or later.
Postgres 7.2 or later.
- Oracle 9iR2 or later.
- SQLite 3.0. (Not recommended for production)
-
-o Apache version 1.3.x or 2.x (http://httpd.apache.org)
- with mod_perl -- (http://perl.apache.org )
- or with FastCGI -- (www.fastcgi.com)
- or other webserver with FastCGI support
-
- RT's FastCGI handler needs to access RT's configuration file.
-
-o Various and sundry perl modules
- A tool included with RT takes care of the installation of
- most of these automatically during the install process.
-
- The tool supplied with RT uses Perl's CPAN system
- (http://www.cpan.org) to install modules. Some operating
- systems package all or some of the modules required, and
- you may be better off installing the modules that way.
-
-
-GENERAL INSTALLATION
---------------------
-
-This is a rough guide to installing RT. For more detail, you'll
-want to read a more comprehensive installation guide at:
-
- http://wiki.bestpractical.com/index.cgi?InstallationGuides
-
-1 Unpack this distribution other than where you want to install RT
-
- To do this cleanly, run the following command:
-
- tar xzvf rt.tar.gz -C /tmp
-2 Run the "configure" script.
+ Mysql 3.23.46 or newer with support for InnoDB
+ is currently deprecated and will be officially
+ desupported in a future release.
- ./configure --help to see the list of options
- ./configure (with the flags you want)
+o Apache version 1.3.x or 2.x (http://httpd.apache.org)
+ with mod_perl -- (http://perl.apache.org )
+ or a webserver with FastCGI support (www.fastcgi.com)
- RT defaults to installing in /opt/rt3 with MySQL as its database. It
- tries to guess which of www-data, www, apache or nobody your webserver
- will run as, but you can override that behavior. Note that the
- default install directory in /opt/rt3 does not work under SELinux's
- default configuration.
+ mod_perl 2.0 isn't quite ready for prime_time just yet;
+ Best Practical Solutions strongly recommends that sites use
+ Apache 1.3 or FastCGI.
- If you're upgrading RT then it worth to read UPGRADING document at this
- moment. Some extension you're using may have been integrated into
- core. It's recommended to use new clean directory when you're
- upgrading to new major release (for example from 3.6.x to 3.8.x).
+ Compiling mod_perl on Apache 1.3.x as a DSO has been known
+ to have massive stability problems and is not recommended.
-3 Make sure that RT has everything it needs to run.
+ mod_perl 1.x must be build with EVERYTHING=1
- Check for missing dependencies by running:
+ RT's FastCGI handler runs setgid to the 'rt' group to
+ protect RT's database password. You may need to install
+ a special "suidperl" package or reconfigure your perl
+ setup to support "setuid scripts" if you intend to use RT
+ with FastCGI.
- make testdeps
+ Debian GNU/* 3.0+: the package which installs suidperl is
+ called perl-suid, and should work without any tweaking.
-4 If the script reports any missing dependencies, install them by hand
- or run the following command as a user who has permission to install perl
- modules on your system:
+ FreeBSD 4.2+: the package is called sperl, and should
+ install a suidperl that just works
- make fixdeps
+ Conectiva Linux 6.0+: suidperl is installed by default when
+ perl is installed, but the program /bin/suidperl is not setuid.
+ You must use chmod to make it setuid.
- Some modules require user input or environment variables to install correctly,
- so it may be necessary to install them manually.
-5 Check to make sure everything was installed properly.
- make testdeps
+o Various and sundry perl modules
+ A tool included with RT takes care of the installation of
+ most of these automatically during the install process.
- It might sometimes be necessary to run "make fixdeps" several times
- to install all necessary perl modules.
+ The tool supplied with RT uses Perl's CPAN system
+ (http://www.cpan.org) to install modules. Some operating
+ systems package all or some of the modules required and
+ you may be better off installing the modules that way.
-6 If this is a new installation:
- As a user with permission to install RT in your chosen directory, type:
-
- make install
-
- Set up etc/RT_SiteConfig.pm in your RT installation directory.
- You'll need to add any values you need to change from the defaults
- in etc/RT_Config.pm
-
- As a user with permission to read RT's configuration file, type:
-
- make initialize-database
-
- If the make fails, type:
-
- make dropdb
+GENERAL INSTALLATION
+--------------------
- and start over from step 6
+This is a rough guide to installing RT. For more detail, you'll want
+to read 'Chapter 2: Installing' in RT's manual, available at
+http://www.bestpractical.com/rt
-7 If you're upgrading from RT 3.0 or newer:
+1 Unpack this distribution SOMWHERE OTHER THAN where you want to install RT
- Read through the UPGRADING document included in this distribution. If
- you're using MySQL, read through UPGRADING.mysql as well.
+ Granted, you've already got it open. To do this cleanly:
- It includes special upgrade instructions that will help you get this
- new version of RT up and running smoothly.
+ tar xzvf rt.tar.gz -C /tmp
- As a user with permission to install RT in your chosen installation
- directory, type:
+2 Run the "configure" script.
- make upgrade
+ ./configure --help to see the list of options
+ ./configure (with the flags you want)
- This will install new binaries, config files and libraries without
- overwriting your RT database.
+3 Satisfy RT's myriad dependencies.
- Update etc/RT_SiteConfig.pm in your RT installation directory.
- You'll need to add any new values you need to change from the defaults
- in etc/RT_Config.pm
+3.1 Check for compliance:
+
+ perl sbin/rt-test-dependencies \
+ --with-<databasename> --with-<web-environment>
- You may also need to update RT's database. You can do this with
- the rt-setup-database tool. Replace root with the name of the dba
- user on your database (root is the default for MySQL).
+ databasename is one of: mysql, postgres
+ web-environment is one of: fastcgi, modperl1, modperl2
- You will be prompted for your previous version of RT (such as 3.6.4)
- so that we can calculate which database updates to apply
+3.2 If there are unsatisfied dependencies, install them by hand or run:
- You should back up your database before running this command.
+ perl sbin/rt-test-dependencies \
+ --with-<databasename> --with-<web-environment> --install
+
- /opt/rt3/sbin/rt-setup-database --dba root --prompt-for-dba-password --action upgrade
+3.3 Check to make sure everything was installed properly:
- Clear mason cache dir:
+ perl sbin/rt-test-dependencies \
+ --with-<databasename> --with-<web-environment>
- rm -fr /opt/rt3/var/mason_data/obj
+4 Create a group called 'rt'
- Stop and start web-server.
+5a FOR A NEW INSTALLATION:
+
+ As root, type:
+ make install (replace "make" with the local name for
+ Make, if you need to)
+
+ make initialize-database
-8 If you're upgrading from RT 2.0:
- Use the RT::Extension::RT2toRT3 module to upgrade to the current RT
- release. You can download it from CPAN here:
- http://search.cpan.org/dist/RT-Extension-RT2toRT3/
+ If the make fails, type:
+ make dropdb
+ and start over from step 5a
-9 Configure the email and web gateways, as described below.
+5b FOR UPGRADING: (Within the RT 3.0.x series)
- NOTE: root's password for the web interface is "password"
- (without the quotes). Not changing this is a SECURITY risk!
+ As root, type:
+ make upgrade (replace "make" with the local name for
+ Make, if you need to)
-10 Set up automated recurring tasks (cronjobs):
+ This will build new binaries, config files and libraries without
+ overwriting your RT database.
+
+ It may then instruct you to update your RT system database objects
- To generate email digest messages, you must arrange for the provided
- utility to be run once daily, and once weekly. You may also want to
- arrange for the rt-email-dashboards utility to be run hourly.
- For example, if your task scheduler is cron, you can configure it as
- follows:
+6 Edit etc/RT_SiteConfig.pm in your RT installation directory, by specifying
+ any values you need to change from the defaults in etc/RT_Config.pm
- crontab -e # as the RT administrator (probably root)
- # insert the following lines:
- 0 0 * * * /opt/rt3/sbin/rt-email-digest -m daily
- 0 0 * * 0 /opt/rt3/sbin/rt-email-digest -m weekly
- 0 * * * * /opt/rt3/sbin/rt-email-dashboards
+7 Configure the email and web gateways, as described below.
+8 Stop and start your webserver, so it picks up your configuration changes.
-11 Set up users, groups, queues, scrips and access control.
+ NOTE: root's password for the web interface is "password"
+ (without the quotes.) Not changing this is a SECURITY risk
+
+9 Configure RT per the instructions in RT's manual.
Until you do this, RT will not be able to send or receive email,
nor will it be more than marginally functional. This is not an
optional step.
+THE WEB INTERFACE
+-----------------
+RT's web interface is based around HTML::Mason, which works best with the mod_perl
+perl interpreter within Apache httpd. Alternatively, support for the FastCGI
+(and plain CGI) interface is also provided as 'bin/mason_handler.fcgi'.
-SETTING UP THE WEB INTERFACE
-----------------------------
-
-RT's web interface is based around HTML::Mason, which works well with
-the mod_perl perl interpreter within Apache httpd and FastCGI.
-
-Once you've set up the web interface, consider setting up automatic
-logout for inactive sessions. For more information about how to do that,
-run
- perldoc /path/to/rt/sbin/rt-clean-sessions
-
-
-mod_perl 1.xx
--------------
-
-WARNING: mod_perl 1.99_xx is not supported.
-
-See below configuration instructions for mod_perl 2.x
-
-To install RT with mod_perl 1.x, you'll need to install the
-apache database connection cache. To make sure it's installed, run
-the following command:
-
- perl -MCPAN -e'install "Apache::DBI"'
-
-Next, add a few lines to your Apache 1.3.xx configuration file, so that
-it knows where to find RT:
+Apache
+ You'll need to add a few lines to your httpd.conf telling it about RT:
<VirtualHost your.ip.address>
ServerName your.rt.server.hostname
-
DocumentRoot /opt/rt3/share/html
AddDefaultCharset UTF-8
- # optional apache logs for RT
- # ErrorLog /opt/rt3/var/log/apache.error
- # TransferLog /opt/rt3/var/log/apache.access
+ # this line applies to Apache2+mod_perl2 only
+ PerlModule Apache2 Apache::compat
PerlModule Apache::DBI
PerlRequire /opt/rt3/bin/webmux.pl
- <Location /NoAuth/images>
- SetHandler default
- </Location>
+ # this section applies to Apache 1 only
<Location />
SetHandler perl-script
PerlHandler RT::Mason
</Location>
-</VirtualHost>
-
-mod_perl 2.xx
--------------
-
-WARNING: mod_perl 1.99_xx is not supported.
-
-Add a few lines to your Apache 2.xx configuration file, so that
-it knows where to find RT:
-
-<VirtualHost your.ip.address>
- ServerName your.rt.server.hostname
-
- DocumentRoot /opt/rt3/share/html
- AddDefaultCharset UTF-8
-
- # optional apache logs for RT
- # ErrorLog /opt/rt3/var/log/apache2.error
- # TransferLog /opt/rt3/var/log/apache2.access
- PerlRequire "/opt/rt3/bin/webmux.pl"
-
- <Location /NoAuth/images>
- SetHandler default
- </Location>
- <Location />
+ # this section applies to Apache2+mod_perl2 only
+ <FilesMatch "\.html$">
SetHandler perl-script
- PerlResponseHandler RT::Mason
- </Location>
-</VirtualHost>
-
-FastCGI
--------
-
-Installation with FastCGI is a little bit more complex and is documented
-in detail at http://wiki.bestpractical.com/index.cgi?FastCGIConfiguration
-
-In the most basic configuration, you can set up your webserver to run
-as a user who is a member of the "rt" unix group so that the FastCGI script
-can read RT's configuration file. It's important to understand the security
-implications of this configuration, which are discussed in the document
-mentioned above.
-
-To install RT with FastCGI, you'll need to add a few lines to your
-Apache configuration file telling it about RT:
-
-
-# Tell FastCGI to put its temporary files somewhere sane.
-FastCgiIpcDir /tmp
-
-FastCgiServer /opt/rt3/bin/mason_handler.fcgi -idle-timeout 120
-
-<VirtualHost rt.example.com>
- ServerName your.rt.server.hostname
-
- # Pass through requests to display images
- Alias /NoAuth/images/ /opt/rt3/share/html/NoAuth/images/
-
- AddHandler fastcgi-script fcgi
- ScriptAlias / /opt/rt3/bin/mason_handler.fcgi/
+ PerlHandler RT::Mason
+ </FilesMatch>
+ <LocationMatch "/Attachment/">
+ SetHandler perl-script
+ PerlHandler RT::Mason
+ </LocationMatch>
+ <LocationMatch "/REST/">
+ SetHandler perl-script
+ PerlHandler RT::Mason
+ </LocationMatch>
</VirtualHost>
-SETTING UP THE MAIL GATEWAY
+SETTING UP THE MAIL GATEWAY
---------------------------
-To let email flow to your RT server, you need to add a few lines of
-configuration to your mail server's "aliases" file. These lines "pipe"
-incoming email messages from your mail server to RT.
+An alias for the initial queue will need to be made in either your
+global mail aliases file (if you are using NIS) or locally on your
+machine.
+
+Add the following lines to /etc/aliases (or your local equivalent) :
-Add the following lines to /etc/aliases (or your local equivalent) on your mail server:
+rt: "|/opt/rt3/bin/rt-mailgate --queue general --action correspond --url http://localhost/"
+rt-comment: "|/opt/rt3/bin/rt-mailgate --queue general --action comment --url http://localhost/"
+ | | |
+ <queue-name>----/ | |
+ | |
+ <correspond or comment depending on whether | |
+ the mail should be resent to the requestor>---/ |
+ |
+ <URL for RT's web interface>---/
-rt: "|/opt/rt3/bin/rt-mailgate --queue general --action correspond --url http://rt.example.com/"
-rt-comment: "|/opt/rt3/bin/rt-mailgate --queue general --action comment --url http://rt.example.com/"
-
-You'll need to add similar lines for each queue you want to be able
-to send email to. To find out more about how to configure RT's email
-gateway, type:
-
- perldoc /opt/rt3/bin/rt-mailgate
+BUGS
+----
+To report a bug, send email to rt-3.0-bugs@fsck.com.
GETTING HELP
------------
If RT is mission-critical for you or if you use it heavily, we recommend that
you purchase a commercial support contract. Details on support contracts
-are available at http://www.bestpractical.com or by writing to
-<sales@bestpractical.com>.
+are available at http://www.bestpractical.com.
If you're interested in having RT extended or customized or would like more
-information about commercial support options, please send email to
+information about commercial support options, please send email to
<sales@bestpractical.com> to discuss rates and availability.
-
-RT WEBSITE
-----------
-
-For current information about RT, check out the RT website at
- http://www.bestpractical.com/
-
-You'll find screenshots, a pointer to the current version of RT, contributed
-patches, and lots of other great stuff.
-
-
-
-RT-USERS MAILING LIST
----------------------
+RT-USERS MAILINGLIST
+--------------------
To keep up to date on the latest RT tips, techniques and extensions,
you probably want to join the rt-users mailing list. Send a message to:
- rt-users-request@lists.bestpractical.com
+ rt-users-request@lists.fsck.com
-with the body of the message consisting of only the word:
+With the body of the message consisting of only the word:
- subscribe
+ subscribe
If you're interested in hacking on RT, you'll want to subscribe to
-<rt-devel@lists.bestpractical.com>. Subscribe to it with instructions
-similar to those above.
+rt-devel@lists.fsck.com. Subscribe to it with instructions similar to
+those above.
Address questions about the stable release to the rt-users list, and
questions about the development version to the rt-devel list. If you feel
@@ -371,63 +280,21 @@ your questions are best not asked publicly, send them personally to
<jesse@bestpractical.com>.
+RT WEBSITE
+----------
-BUGS
-----
+For current information about RT, check out the RT website at
+ http://www.bestpractical.com/
-RT's a pretty complex application, and as you get up to speed, you might
-run into some trouble. Generally, it's best to ask about things you
-run into on the rt-users mailinglist (or pick up a commercial support
-contract from Best Practical). But, sometimes people do run into bugs. In
-the exceedingly unlikely event that you hit a bug in RT, please report
-it! We'd love to hear about problems you have with RT, so we can fix them.
-To report a bug, send email to rt-bugs@fsck.com.
+You'll find screenshots, a pointer to the current version of RT, contributed
+patches, and lots of other great stuff.
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
+TROUBLESHOOTING
+---------------
+
+If the solution to the problem you're running into isn't obvious and you've
+checked the FAQ, feel free to send mail to rt-users@fsck.com (for released
+versions of RT) or rt-devel@fsck.com (for development versions).
+
+Thanks!
diff --git a/rt/bin/mason_handler.fcgi b/rt/bin/mason_handler.fcgi
index 4fe888a93..38f590124 100755
--- a/rt/bin/mason_handler.fcgi
+++ b/rt/bin/mason_handler.fcgi
@@ -2,8 +2,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -25,7 +25,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+# http://www.gnu.org/copyleft/gpl.html.
#
#
# CONTRIBUTION SUBMISSION POLICY:
@@ -51,17 +51,12 @@ package RT::Mason;
use strict;
use vars '$Handler';
use File::Basename;
-
-require (dirname(__FILE__) . '/webmux.pl');
+require ('/opt/rt3/bin/webmux.pl');
# Enter CGI::Fast mode, which should also work as a vanilla CGI script.
require CGI::Fast;
RT::Init();
-$Handler ||= RT::Interface::Web::Handler->new(
- RT->Config->Get('MasonParameters')
-);
-
while ( my $cgi = CGI::Fast->new ) {
# the whole point of fastcgi requires the env to get reset here..
@@ -72,7 +67,7 @@ while ( my $cgi = CGI::Fast->new ) {
$ENV{'ENV'} = '' if defined $ENV{'ENV'};
$ENV{'IFS'} = '' if defined $ENV{'IFS'};
- Module::Refresh->refresh if RT->Config->Get('DevelMode');
+ Module::Refresh->refresh if $RT::DevelMode;
RT::ConnectToDatabase();
if ( ( !$Handler->interp->comp_exists( $cgi->path_info ) )
diff --git a/rt/bin/mason_handler.scgi b/rt/bin/mason_handler.scgi
index 248c57ca2..faff8a5db 100755
--- a/rt/bin/mason_handler.scgi
+++ b/rt/bin/mason_handler.scgi
@@ -2,8 +2,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -25,7 +25,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+# http://www.gnu.org/copyleft/gpl.html.
#
#
# CONTRIBUTION SUBMISSION POLICY:
@@ -50,17 +50,11 @@ package RT::Mason;
use strict;
use vars '$Handler';
-use File::Basename;
-
-require (dirname(__FILE__) . '/webmux.pl');
+require ('/opt/rt3/bin/webmux.pl');
require CGI;
RT::Init();
-$Handler ||= RT::Interface::Web::Handler->new(
- RT->Config->Get('MasonParameters')
-);
-
my $cgi = CGI->new;
if ( ( !$Handler->interp->comp_exists( $cgi->path_info ) )
diff --git a/rt/bin/mason_handler.svc b/rt/bin/mason_handler.svc
index 3cde20cd1..fc97da9b9 100644
--- a/rt/bin/mason_handler.svc
+++ b/rt/bin/mason_handler.svc
@@ -2,8 +2,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -25,7 +25,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+# http://www.gnu.org/copyleft/gpl.html.
#
#
# CONTRIBUTION SUBMISSION POLICY:
@@ -111,7 +111,7 @@ BEGIN {
$Win32::TieRegistry::Registry->{
'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\\'.
'W3SVC\Parameters\Virtual Roots\\'
- }->{RT->Config->Get('WebPath') || '/'} = "$path,,205";
+ }->{$RT::WebPath || '/'} = "$path,,205";
$Win32::TieRegistry::Registry->{
'HKEY_LOCAL_MACHINE\Software\FASTCGI\.html\\'
@@ -225,18 +225,13 @@ warn "Begin listening on $ENV{'FCGI_SOCKET_PATH'}\n";
require CGI::Fast;
RT::Init();
-$Handler ||= RT::Interface::Web::Handler->new(
- RT->Config->Get('MasonParameters')
-);
-
# Response loop
while( my $cgi = CGI::Fast->new ) {
my $comp = $ENV{'PATH_INFO'};
$comp = $1 if ($comp =~ /^(.*)$/);
- my $web_path = RT->Config->Get('WebPath');
- $comp =~ s|^\Q$web_path\E\b||i;
+ $comp =~ s|^$RT::WebPath\b||i;
$comp .= "index.html" if ($comp =~ /\/$/);
$comp =~ s/.pl$/.html/g;
diff --git a/rt/bin/rt b/rt/bin/rt
deleted file mode 100755
index 9554a932c..000000000
--- a/rt/bin/rt
+++ /dev/null
@@ -1,2589 +0,0 @@
-#!/usr/bin/perl -w
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-# Designed and implemented for Best Practical Solutions, LLC by
-# Abhijit Menon-Sen <ams@wiw.org>
-
-use strict;
-
-# This program is intentionally written to have as few non-core module
-# dependencies as possible. It should stay that way.
-
-use Cwd;
-use LWP;
-use Text::ParseWords;
-use HTTP::Request::Common;
-use HTTP::Headers;
-use Term::ReadLine;
-use Time::Local; # used in prettyshow
-
-# strong (GSSAPI based) authentication is supported if the server does provide
-# it and the perl modules GSSAPI and LWP::Authen::Negotiate are installed
-# it can be suppressed by setting externalauth=0 (default is undef)
-eval { require GSSAPI };
-my $no_strong_auth = 'missing perl module GSSAPI';
-if ( ! $@ ) {
- eval {require LWP::Authen::Negotiate};
- $no_strong_auth = $@ ? 'missing perl module LWP::Authen::Negotiate' : 0;
-}
-
-# We derive configuration information from hardwired defaults, dotfiles,
-# and the RT* environment variables (in increasing order of precedence).
-# Session information is stored in ~/.rt_sessions.
-
-my $VERSION = 0.02;
-my $HOME = eval{(getpwuid($<))[7]}
- || $ENV{HOME} || $ENV{LOGDIR} || $ENV{HOMEPATH}
- || ".";
-my %config = (
- (
- debug => 0,
- user => eval{(getpwuid($<))[0]} || $ENV{USER} || $ENV{USERNAME},
- passwd => undef,
- server => 'http://localhost/',
- query => "Status!='resolved' and Status!='rejected'",
- orderby => 'id',
- queue => undef,
-# to protect against unlimited searches a better choice would be
-# queue => 'Unknown_Queue',
-# setting externalauth => undef will try GSSAPI auth if the corresponding perl
-# modules are installed, externalauth => 0 is the backward compatible choice
- externalauth => 0,
- ),
- config_from_file($ENV{RTCONFIG} || ".rtrc"),
- config_from_env()
-);
-my $session = new Session("$HOME/.rt_sessions");
-my $REST = "$config{server}/REST/1.0";
-$no_strong_auth = 'switched off by externalauth=0'
- if defined $config{externalauth};
-
-
-my $prompt = 'rt> ';
-
-sub whine;
-sub DEBUG { warn @_ if $config{debug} >= shift }
-
-# These regexes are used by command handlers to parse arguments.
-# (XXX: Ask Autrijus how i18n changes these definitions.)
-
-my $name = '[\w.-]+';
-my $CF_name = '[\sa-z0-9_ :()/-]+';
-my $field = '(?i:[a-z][a-z0-9_-]*|C(?:ustom)?F(?:ield)?-'.$CF_name.'|CF\.\{'.$CF_name.'\})';
-my $label = '[a-zA-Z0-9@_.+-]+';
-my $labels = "(?:$label,)*$label";
-my $idlist = '(?:(?:\d+-)?\d+,)*(?:\d+-)?\d+';
-
-# Our command line looks like this:
-#
-# rt <action> [options] [arguments]
-#
-# We'll parse just enough of it to decide upon an action to perform, and
-# leave the rest to per-action handlers to interpret appropriately.
-
-my %handlers = (
-# handler => [ ...aliases... ],
- version => ["version", "ver"],
- shell => ["shell"],
- logout => ["logout"],
- help => ["help", "man"],
- show => ["show", "cat"],
- edit => ["create", "edit", "new", "ed"],
- list => ["search", "list", "ls"],
- comment => ["comment", "correspond"],
- link => ["link", "ln"],
- merge => ["merge"],
- grant => ["grant", "revoke"],
- take => ["take", "steal", "untake"],
- quit => ["quit", "exit"],
- setcommand => ["del", "delete", "give", "res", "resolve",
- "subject"],
-);
-
-my %actions;
-foreach my $fn (keys %handlers) {
- foreach my $alias (@{ $handlers{$fn} }) {
- $actions{$alias} = \&{"$fn"};
- }
-}
-
-# Once we find and call an appropriate handler, we're done.
-
-sub handler {
- my $action;
-
- push @ARGV, 'shell' if (!@ARGV); # default to shell mode
- shift @ARGV if ($ARGV[0] eq 'rt'); # ignore a leading 'rt'
- if (@ARGV && exists $actions{$ARGV[0]}) {
- $action = shift @ARGV;
- return $actions{$action}->($action);
- }
- else {
- print STDERR "rt: Unknown command '@ARGV'.\n";
- print STDERR "rt: For help, run 'rt help'.\n";
- return 1;
- }
-}
-
-exit handler();
-
-# Handler functions.
-# ------------------
-#
-# The following subs are handlers for each entry in %actions.
-
-sub shell {
- $|=1;
- my $term = new Term::ReadLine 'RT CLI';
- while ( defined ($_ = $term->readline($prompt)) ) {
- next if /^#/ || /^\s*$/;
-
- @ARGV = shellwords($_);
- handler();
- }
-}
-
-sub version {
- print "rt $VERSION\n";
- return 0;
-}
-
-sub logout {
- submit("$REST/logout") if defined $session->cookie;
- return 0;
-}
-
-sub quit {
- logout();
- exit;
-}
-
-my %help;
-sub help {
- my ($action, $type, $rv) = @_;
- $rv = defined $rv ? $rv : 0;
- my $key;
-
- # What help topics do we know about?
- if (!%help) {
- local $/ = undef;
- foreach my $item (@{ Form::parse(<DATA>) }) {
- my $title = $item->[2]{Title};
- my @titles = ref $title eq 'ARRAY' ? @$title : $title;
-
- foreach $title (grep $_, @titles) {
- $help{$title} = $item->[2]{Text};
- }
- }
- }
-
- # What does the user want help with?
- undef $action if ($action && $actions{$action} eq \&help);
- unless ($action || $type) {
- # If we don't know, we'll look for clues in @ARGV.
- foreach (@ARGV) {
- if (exists $help{$_}) { $key = $_; last; }
- }
- unless ($key) {
- # Tolerate possibly plural words.
- foreach (@ARGV) {
- if ($_ =~ s/s$// && exists $help{$_}) { $key = $_; last; }
- }
- }
- }
-
- if ($type && $action) {
- $key = "$type.$action";
- }
- $key ||= $type || $action || "introduction";
-
- # Find a suitable topic to display.
- while (!exists $help{$key}) {
- if ($type && $action) {
- if ($key eq "$type.$action") { $key = $action; }
- elsif ($key eq $action) { $key = $type; }
- else { $key = "introduction"; }
- }
- else {
- $key = "introduction";
- }
- }
-
- print STDERR $help{$key}, "\n\n";
- return $rv;
-}
-
-# Displays a list of objects that match some specified condition.
-
-sub list {
- my ($q, $type, %data);
- my $orderby = $config{orderby};
-
- if ($config{orderby}) {
- $data{orderby} = $config{orderby};
- }
- my $bad = 0;
- my $rawprint = 0;
- my $reverse_sort = 0;
- my $queue = $config{queue};
-
- while (@ARGV) {
- $_ = shift @ARGV;
-
- if (/^-t$/) {
- $bad = 1, last unless defined($type = get_type_argument());
- }
- elsif (/^-S$/) {
- $bad = 1, last unless get_var_argument(\%data);
- }
- elsif (/^-o$/) {
- $data{'orderby'} = shift @ARGV;
- }
- elsif (/^-([isl])$/) {
- $data{format} = $1;
- $rawprint = 1;
- }
- elsif (/^-q$/) {
- $queue = shift @ARGV;
- }
- elsif (/^-r$/) {
- $reverse_sort = 1;
- }
- elsif (/^-f$/) {
- if ($ARGV[0] !~ /^(?:(?:$field,)*$field)$/) {
- whine "No valid field list in '-f $ARGV[0]'.";
- $bad = 1; last;
- }
- $data{fields} = shift @ARGV;
- $data{format} = 's' if ! $data{format};
- $rawprint = 1;
- }
- elsif (!defined $q && !/^-/) {
- $q = $_;
- }
- else {
- my $datum = /^-/ ? "option" : "argument";
- whine "Unrecognised $datum '$_'.";
- $bad = 1; last;
- }
- }
- if ( ! $rawprint and ! exists $data{format} ) {
- $data{format} = 'l';
- }
- if ( $reverse_sort and $data{orderby} =~ /^-/ ) {
- $data{orderby} =~ s/^-/+/;
- } elsif ($reverse_sort) {
- $data{orderby} =~ s/^\+?(.*)/-$1/;
- }
-
- if (!defined $q) {
- $q = $config{query};
- }
-
- $q =~ s/^#//; # get rid of leading hash
- if ($q =~ /^\d+$/) {
- # only digits, must be an id, formulate a correct query
- $q = "id=$q" if $q =~ /^\d+$/;
- } else {
- # a string only, take it as an owner or requestor (quoting done later)
- $q = "(Owner=$q or Requestor like $q) and $config{query}"
- if $q =~ /^[\w\-]+$/;
- # always add a query for a specific queue or (comma separated) queues
- $queue =~ s/,/ or Queue=/g if $queue;
- $q .= " and (Queue=$queue)" if $queue and $q and $q !~ /Queue\s*=/i
- and $q !~ /id\s*=/i;
- }
- # correctly quote strings in a query
- $q =~ s/(=|like\s)\s*([^'\d\s]\S*)\b/$1\'$2\'/g;
-
- $type ||= "ticket";
- unless ($type && defined $q) {
- my $item = $type ? "query string" : "object type";
- whine "No $item specified.";
- $bad = 1;
- }
- #return help("list", $type) if $bad;
- return suggest_help("list", $type, $bad) if $bad;
-
- print "Query:$q\n" if ! $rawprint;
- my $r = submit("$REST/search/$type", { query => $q, %data });
- if ( $rawprint ) {
- print $r->content;
- } else {
- my $forms = Form::parse($r->content);
- prettylist ($forms);
- }
- return 0;
-}
-
-# Displays selected information about a single object.
-
-sub show {
- my ($type, @objects, %data);
- my $slurped = 0;
- my $bad = 0;
- my $rawprint = 0;
- my $histspec;
-
- while (@ARGV) {
- $_ = shift @ARGV;
- s/^#// if /^#\d+/; # get rid of leading hash
- if (/^-t$/) {
- $bad = 1, last unless defined($type = get_type_argument());
- }
- elsif (/^-S$/) {
- $bad = 1, last unless get_var_argument(\%data);
- }
- elsif (/^-([isl])$/) {
- $data{format} = $1;
- $rawprint = 1;
- }
- elsif (/^-$/ && !$slurped) {
- chomp(my @lines = <STDIN>);
- foreach (@lines) {
- unless (is_object_spec($_, $type)) {
- whine "Invalid object on STDIN: '$_'.";
- $bad = 1; last;
- }
- push @objects, $_;
- }
- $slurped = 1;
- }
- elsif (/^-f$/) {
- if ($ARGV[0] !~ /^(?:(?:$field,)*$field)$/) {
- whine "No valid field list in '-f $ARGV[0]'.";
- $bad = 1; last;
- }
- $data{fields} = shift @ARGV;
- # option f requires short raw listing format
- $data{format} = 's';
- $rawprint = 1;
- }
- elsif (/^\d+$/ and my $spc2 = is_object_spec("ticket/$_", $type)) {
- push @objects, $spc2;
- $histspec = is_object_spec("ticket/$_/history", $type);
- }
- elsif (/^\d+\// and my $spc3 = is_object_spec("ticket/$_", $type)) {
- push @objects, $spc3;
- $rawprint = 1 if $_ =~ /\/content$/;
- }
- elsif (my $spec = is_object_spec($_, $type)) {
- push @objects, $spec;
- $rawprint = 1 if $_ =~ /\/content$/ or $_ !~ /^ticket/;
- }
- else {
- my $datum = /^-/ ? "option" : "argument";
- whine "Unrecognised $datum '$_'.";
- $bad = 1; last;
- }
- }
- if ( ! $rawprint ) {
- push @objects, $histspec if $histspec;
- $data{format} = 'l' if ! exists $data{format};
- }
-
- unless (@objects) {
- whine "No objects specified.";
- $bad = 1;
- }
- #return help("show", $type) if $bad;
- return suggest_help("show", $type, $bad) if $bad;
-
- my $r = submit("$REST/show", { id => \@objects, %data });
- my $c = $r->content;
- # if this isn't a text reply, remove the trailing newline so we
- # don't corrupt things like tarballs when people do
- # show ticket/id/attachments/id/content > foo.tar.gz
- if ($r->content_type !~ /^text\//) {
- chomp($c);
- $rawprint = 1;
- }
- if ( $rawprint ) {
- print $c;
- } else {
- # I do not know how to get more than one form correctly returned
- $c =~ s!^RT/[\d\.]+ 200 Ok$!--!mg;
- my $forms = Form::parse($c);
- prettyshow ($forms);
- }
- return 0;
-}
-
-# To create a new object, we ask the server for a form with the defaults
-# filled in, allow the user to edit it, and send the form back.
-#
-# To edit an object, we must ask the server for a form representing that
-# object, make changes requested by the user (either on the command line
-# or interactively via $EDITOR), and send the form back.
-
-sub edit {
- my ($action) = @_;
- my (%data, $type, @objects);
- my ($cl, $text, $edit, $input, $output);
-
- use vars qw(%set %add %del);
- %set = %add = %del = ();
- my $slurped = 0;
- my $bad = 0;
-
- while (@ARGV) {
- $_ = shift @ARGV;
- s/^#// if /^#\d+/; # get rid of leading hash
-
- if (/^-e$/) { $edit = 1 }
- elsif (/^-i$/) { $input = 1 }
- elsif (/^-o$/) { $output = 1 }
- elsif (/^-t$/) {
- $bad = 1, last unless defined($type = get_type_argument());
- }
- elsif (/^-S$/) {
- $bad = 1, last unless get_var_argument(\%data);
- }
- elsif (/^-$/ && !($slurped || $input)) {
- chomp(my @lines = <STDIN>);
- foreach (@lines) {
- unless (is_object_spec($_, $type)) {
- whine "Invalid object on STDIN: '$_'.";
- $bad = 1; last;
- }
- push @objects, $_;
- }
- $slurped = 1;
- }
- elsif (/^set$/i) {
- my $vars = 0;
-
- while (@ARGV && $ARGV[0] =~ /^($field)([+-]?=)(.*)$/s) {
- my ($key, $op, $val) = ($1, $2, $3);
- my $hash = ($op eq '=') ? \%set : ($op =~ /^\+/) ? \%add : \%del;
-
- vpush($hash, lc $key, $val);
- shift @ARGV;
- $vars++;
- }
- unless ($vars) {
- whine "No variables to set.";
- $bad = 1; last;
- }
- $cl = $vars;
- }
- elsif (/^(?:add|del)$/i) {
- my $vars = 0;
- my $hash = ($_ eq "add") ? \%add : \%del;
-
- while (@ARGV && $ARGV[0] =~ /^($field)=(.*)$/s) {
- my ($key, $val) = ($1, $2);
-
- vpush($hash, lc $key, $val);
- shift @ARGV;
- $vars++;
- }
- unless ($vars) {
- whine "No variables to set.";
- $bad = 1; last;
- }
- $cl = $vars;
- }
- elsif (/^\d+$/ and my $spc2 = is_object_spec("ticket/$_", $type)) {
- push @objects, $spc2;
- }
- elsif (my $spec = is_object_spec($_, $type)) {
- push @objects, $spec;
- }
- else {
- my $datum = /^-/ ? "option" : "argument";
- whine "Unrecognised $datum '$_'.";
- $bad = 1; last;
- }
- }
-
- if ($action =~ /^ed(?:it)?$/) {
- unless (@objects) {
- whine "No objects specified.";
- $bad = 1;
- }
- }
- else {
- if (@objects) {
- whine "You shouldn't specify objects as arguments to $action.";
- $bad = 1;
- }
- unless ($type) {
- whine "What type of object do you want to create?";
- $bad = 1;
- }
- @objects = ("$type/new") if defined($type);
- }
- #return help($action, $type) if $bad;
- return suggest_help($action, $type, $bad) if $bad;
-
- # We need a form to make changes to. We usually ask the server for
- # one, but we can avoid that if we are fed one on STDIN, or if the
- # user doesn't want to edit the form by hand, and the command line
- # specifies only simple variable assignments. We *should* get a
- # form if we're creating a new ticket, so that the default values
- # get filled in properly.
-
- my @new_objects = grep /\/new$/, @objects;
-
- if ($input) {
- local $/ = undef;
- $text = <STDIN>;
- }
- elsif ($edit || %add || %del || !$cl || @new_objects) {
- my $r = submit("$REST/show", { id => \@objects, format => 'l' });
- $text = $r->content;
- }
-
- # If any changes were specified on the command line, apply them.
- if ($cl) {
- if ($text) {
- # We're updating forms from the server.
- my $forms = Form::parse($text);
-
- foreach my $form (@$forms) {
- my ($c, $o, $k, $e) = @$form;
- my ($key, $val);
-
- next if ($e || !@$o);
-
- local %add = %add;
- local %del = %del;
- local %set = %set;
-
- # Make changes to existing fields.
- foreach $key (@$o) {
- if (exists $add{lc $key}) {
- $val = delete $add{lc $key};
- vpush($k, $key, $val);
- $k->{$key} = vsplit($k->{$key}) if $val =~ /[,\n]/;
- }
- if (exists $del{lc $key}) {
- $val = delete $del{lc $key};
- my %val = map {$_=>1} @{ vsplit($val) };
- $k->{$key} = vsplit($k->{$key});
- @{$k->{$key}} = grep {!exists $val{$_}} @{$k->{$key}};
- }
- if (exists $set{lc $key}) {
- $k->{$key} = delete $set{lc $key};
- }
- }
-
- # Then update the others.
- foreach $key (keys %set) { vpush($k, $key, $set{$key}) }
- foreach $key (keys %add) {
- vpush($k, $key, $add{$key});
- $k->{$key} = vsplit($k->{$key});
- }
- push @$o, (keys %add, keys %set);
- }
-
- $text = Form::compose($forms);
- }
- else {
- # We're rolling our own set of forms.
- my @forms;
- foreach (@objects) {
- my ($type, $ids, $args) =
- m{^($name)/($idlist|$labels)(?:(/.*))?$}o;
-
- $args ||= "";
- foreach my $obj (expand_list($ids)) {
- my %set = (%set, id => "$type/$obj$args");
- push @forms, ["", [keys %set], \%set];
- }
- }
- $text = Form::compose(\@forms);
- }
- }
-
- if ($output) {
- print $text;
- return 0;
- }
-
- my $synerr = 0;
-
-EDIT:
- # We'll let the user edit the form before sending it to the server,
- # unless we have enough information to submit it non-interactively.
- if ($edit || (!$input && !$cl)) {
- my $newtext = vi($text);
- # We won't resubmit a bad form unless it was changed.
- $text = ($synerr && $newtext eq $text) ? undef : $newtext;
- }
-
- if ($text) {
- my $r = submit("$REST/edit", {content => $text, %data});
- if ($r->code == 409) {
- # If we submitted a bad form, we'll give the user a chance
- # to correct it and resubmit.
- if ($edit || (!$input && !$cl)) {
- $text = $r->content;
- $synerr = 1;
- goto EDIT;
- }
- else {
- print $r->content;
- return 0;
- }
- }
- print $r->content;
- }
- return 0;
-}
-
-# handler for special edit commands. A valid edit command is constructed and
-# further work is delegated to the edit handler
-
-sub setcommand {
- my ($action) = @_;
- my ($id, $bad, $what);
- if ( @ARGV ) {
- $_ = shift @ARGV;
- $id = $1 if (m|^(?:ticket/)?($idlist)$|);
- }
- if ( ! $id ) {
- $bad = 1;
- whine "No ticket number specified.";
- }
- if ( @ARGV ) {
- if ($action eq 'subject') {
- my $subject = '"'.join (" ", @ARGV).'"';
- @ARGV = ();
- $what = "subject=$subject";
- } elsif ($action eq 'give') {
- my $owner = shift @ARGV;
- $what = "owner=$owner";
- }
- } else {
- if ( $action eq 'delete' or $action eq 'del' ) {
- $what = "status=deleted";
- } elsif ($action eq 'resolve' or $action eq 'res' ) {
- $what = "status=resolved";
- } elsif ($action eq 'take' ) {
- $what = "owner=$config{user}";
- } elsif ($action eq 'untake') {
- $what = "owner=Nobody";
- }
- }
- if (@ARGV) {
- $bad = 1;
- whine "Extraneous arguments for action $action: @ARGV.";
- }
- if ( ! $what ) {
- $bad = 1;
- whine "unrecognized action $action.";
- }
- return help("edit", undef, $bad) if $bad;
- @ARGV = ( $id, "set", $what );
- print "Executing: rt edit @ARGV\n";
- return edit("edit");
-}
-
-# We roll "comment" and "correspond" into the same handler.
-
-sub comment {
- my ($action) = @_;
- my (%data, $id, @files, @bcc, @cc, $msg, $wtime, $edit);
- my $bad = 0;
-
- while (@ARGV) {
- $_ = shift @ARGV;
-
- if (/^-e$/) {
- $edit = 1;
- }
- elsif (/^-[abcmw]$/) {
- unless (@ARGV) {
- whine "No argument specified with $_.";
- $bad = 1; last;
- }
-
- if (/-a/) {
- unless (-f $ARGV[0] && -r $ARGV[0]) {
- whine "Cannot read attachment: '$ARGV[0]'.";
- return 0;
- }
- push @files, shift @ARGV;
- }
- elsif (/-([bc])/) {
- my $a = $_ eq "-b" ? \@bcc : \@cc;
- @$a = split /\s*,\s*/, shift @ARGV;
- }
- elsif (/-m/) {
- $msg = shift @ARGV;
- if ( $msg =~ /^-$/ ) {
- undef $msg;
- while (<STDIN>) { $msg .= $_ }
- }
- }
-
- elsif (/-w/) { $wtime = shift @ARGV }
- }
- elsif (!$id && m|^(?:ticket/)?($idlist)$|) {
- $id = $1;
- }
- else {
- my $datum = /^-/ ? "option" : "argument";
- whine "Unrecognised $datum '$_'.";
- $bad = 1; last;
- }
- }
-
- unless ($id) {
- whine "No object specified.";
- $bad = 1;
- }
- #return help($action, "ticket") if $bad;
- return suggest_help($action, "ticket") if $bad;
-
- my $form = [
- "",
- [ "Ticket", "Action", "Cc", "Bcc", "Attachment", "TimeWorked", "Text" ],
- {
- Ticket => $id,
- Action => $action,
- Cc => [ @cc ],
- Bcc => [ @bcc ],
- Attachment => [ @files ],
- TimeWorked => $wtime || '',
- Text => $msg || '',
- Status => ''
- }
- ];
-
- my $text = Form::compose([ $form ]);
-
- if ($edit || !$msg) {
- my $error = 0;
- my ($c, $o, $k, $e);
-
- do {
- my $ntext = vi($text);
- return if ($error && $ntext eq $text);
- $text = $ntext;
- $form = Form::parse($text);
- $error = 0;
-
- ($c, $o, $k, $e) = @{ $form->[0] };
- if ($e) {
- $error = 1;
- $c = "# Syntax error.";
- goto NEXT;
- }
- elsif (!@$o) {
- return 0;
- }
- @files = @{ vsplit($k->{Attachment}) };
-
- NEXT:
- $text = Form::compose([[$c, $o, $k, $e]]);
- } while ($error);
- }
-
- my $i = 1;
- foreach my $file (@files) {
- $data{"attachment_$i"} = bless([ $file ], "Attachment");
- $i++;
- }
- $data{content} = $text;
-
- my $r = submit("$REST/ticket/$id/comment", \%data);
- print $r->content;
- return 0;
-}
-
-# Merge one ticket into another.
-
-sub merge {
- my @id;
- my $bad = 0;
-
- while (@ARGV) {
- $_ = shift @ARGV;
- s/^#// if /^#\d+/; # get rid of leading hash
-
- if (/^\d+$/) {
- push @id, $_;
- }
- else {
- whine "Unrecognised argument: '$_'.";
- $bad = 1; last;
- }
- }
-
- unless (@id == 2) {
- my $evil = @id > 2 ? "many" : "few";
- whine "Too $evil arguments specified.";
- $bad = 1;
- }
- #return help("merge", "ticket") if $bad;
- return suggest_help("merge", "ticket", $bad) if $bad;
-
- my $r = submit("$REST/ticket/$id[0]/merge/$id[1]");
- print $r->content;
- return 0;
-}
-
-# Link one ticket to another.
-
-sub link {
- my ($bad, $del, %data) = (0, 0, ());
- my $type;
-
- my %ltypes = map { lc $_ => $_ } qw(DependsOn DependedOnBy RefersTo
- ReferredToBy HasMember MemberOf);
-
- while (@ARGV && $ARGV[0] =~ /^-/) {
- $_ = shift @ARGV;
-
- if (/^-d$/) {
- $del = 1;
- }
- elsif (/^-t$/) {
- $bad = 1, last unless defined($type = get_type_argument());
- }
- else {
- whine "Unrecognised option: '$_'.";
- $bad = 1; last;
- }
- }
-
- $type = "ticket" unless $type; # default type to tickets
-
- if (@ARGV == 3) {
- my ($from, $rel, $to) = @ARGV;
- if ($from !~ /^\d+$/ || $to !~ /^\d+$/) {
- my $bad = $from =~ /^\d+$/ ? $to : $from;
- whine "Invalid $type ID '$bad' specified.";
- $bad = 1;
- }
- if (($type eq "ticket") && ( ! exists $ltypes{lc $rel})) {
- whine "Invalid link '$rel' for type $type specified.";
- $bad = 1;
- }
- %data = (id => $from, rel => $rel, to => $to, del => $del);
- }
- else {
- my $bad = @ARGV < 3 ? "few" : "many";
- whine "Too $bad arguments specified.";
- $bad = 1;
- }
- return suggest_help("link", $type, $bad) if $bad;
-
- my $r = submit("$REST/$type/link", \%data);
- print $r->content;
- return 0;
-}
-
-# Take/steal a ticket
-sub take {
- my ($cmd) = @_;
- my ($bad, %data) = (0, ());
-
- my $id;
-
- # get the ticket id
- if (@ARGV == 1) {
- ($id) = @ARGV;
- unless ($id =~ /^\d+$/) {
- whine "Invalid ticket ID $id specified.";
- $bad = 1;
- }
- my $form = [
- "",
- [ "Ticket", "Action" ],
- {
- Ticket => $id,
- Action => $cmd,
- Status => '',
- }
- ];
-
- my $text = Form::compose([ $form ]);
- $data{content} = $text;
- }
- else {
- $bad = @ARGV < 1 ? "few" : "many";
- whine "Too $bad arguments specified.";
- $bad = 1;
- }
- return suggest_help("take", "ticket", $bad) if $bad;
-
- my $r = submit("$REST/ticket/$id/take", \%data);
- print $r->content;
- return 0;
-}
-
-# Grant/revoke a user's rights.
-
-sub grant {
- my ($cmd) = @_;
-
- my $revoke = 0;
- while (@ARGV) {
- }
-
- $revoke = 1 if $cmd->{action} eq 'revoke';
- return 0;
-}
-
-# Client <-> Server communication.
-# --------------------------------
-#
-# This function composes and sends an HTTP request to the RT server, and
-# interprets the response. It takes a request URI, and optional request
-# data (a string, or a reference to a set of key-value pairs).
-
-sub submit {
- my ($uri, $content) = @_;
- my ($req, $data);
- my $ua = new LWP::UserAgent(agent => "RT/3.0b", env_proxy => 1);
- my $h = HTTP::Headers->new;
-
- # Did the caller specify any data to send with the request?
- $data = [];
- if (defined $content) {
- unless (ref $content) {
- # If it's just a string, make sure LWP handles it properly.
- # (By pretending that it's a file!)
- $content = [ content => [undef, "", Content => $content] ];
- }
- elsif (ref $content eq 'HASH') {
- my @data;
- foreach my $k (keys %$content) {
- if (ref $content->{$k} eq 'ARRAY') {
- foreach my $v (@{ $content->{$k} }) {
- push @data, $k, $v;
- }
- }
- else { push @data, $k, $content->{$k} }
- }
- $content = \@data;
- }
- $data = $content;
- }
-
- # Should we send authentication information to start a new session?
- my $how = $config{server} =~ /^https/ ? 'over SSL' : 'unencrypted';
- (my $server = $config{server}) =~ s/^.*\/\/([^\/]+)\/?/$1/;
- if ($config{externalauth}) {
- $h->authorization_basic($config{user}, $config{passwd} || read_passwd() );
- print " Password will be sent to $server $how\n",
- " Press CTRL-C now if you do not want to continue\n"
- if ! $config{passwd};
- } elsif ( $no_strong_auth ) {
- if (!defined $session->cookie) {
- print " Strong encryption not available, $no_strong_auth\n",
- " Password will be sent to $server $how\n",
- " Press CTRL-C now if you do not want to continue\n"
- if ! $config{passwd};
- push @$data, ( user => $config{user} );
- push @$data, ( pass => $config{passwd} || read_passwd() );
- }
- }
-
- # Now, we construct the request.
- if (@$data) {
- $req = POST($uri, $data, Content_Type => 'form-data');
- }
- else {
- $req = GET($uri);
- }
- $session->add_cookie_header($req);
- if ($config{externalauth}) {
- $req->header(%$h);
- }
-
- # Then we send the request and parse the response.
- DEBUG(3, $req->as_string);
- my $res = $ua->request($req);
- DEBUG(3, $res->as_string);
-
- if ($res->is_success) {
- # The content of the response we get from the RT server consists
- # of an HTTP-like status line followed by optional header lines,
- # a blank line, and arbitrary text.
-
- my ($head, $text) = split /\n\n/, $res->content, 2;
- my ($status, @headers) = split /\n/, $head;
- $text =~ s/\n*$/\n/ if ($text);
-
- # "RT/3.0.1 401 Credentials required"
- if ($status !~ m#^RT/\d+(?:\S+) (\d+) ([\w\s]+)$#) {
- warn "rt: Malformed RT response from $config{server}.\n";
- warn "(Rerun with RTDEBUG=3 for details.)\n" if $config{debug} < 3;
- exit -1;
- }
-
- # Our caller can pretend that the server returned a custom HTTP
- # response code and message. (Doing that directly is apparently
- # not sufficiently portable and uncomplicated.)
- $res->code($1);
- $res->message($2);
- $res->content($text);
- $session->update($res) if ($res->is_success || $res->code != 401);
-
- if (!$res->is_success) {
- # We can deal with authentication failures ourselves. Either
- # we sent invalid credentials, or our session has expired.
- if ($res->code == 401) {
- my %d = @$data;
- if (exists $d{user}) {
- warn "rt: Incorrect username or password.\n";
- exit -1;
- }
- elsif ($req->header("Cookie")) {
- # We'll retry the request with credentials, unless
- # we only wanted to logout in the first place.
- $session->delete;
- return submit(@_) unless $uri eq "$REST/logout";
- }
- }
- # Conflicts should be dealt with by the handler and user.
- # For anything else, we just die.
- elsif ($res->code != 409) {
- warn "rt: ", $res->content;
- #exit;
- }
- }
- }
- else {
- warn "rt: Server error: ", $res->message, " (", $res->code, ")\n";
- exit -1;
- }
-
- return $res;
-}
-
-# Session management.
-# -------------------
-#
-# Maintains a list of active sessions in the ~/.rt_sessions file.
-{
- package Session;
- my ($s, $u);
-
- # Initialises the session cache.
- sub new {
- my ($class, $file) = @_;
- my $self = {
- file => $file || "$HOME/.rt_sessions",
- sids => { }
- };
-
- # The current session is identified by the currently configured
- # server and user.
- ($s, $u) = @config{"server", "user"};
-
- bless $self, $class;
- $self->load();
-
- return $self;
- }
-
- # Returns the current session cookie.
- sub cookie {
- my ($self) = @_;
- my $cookie = $self->{sids}{$s}{$u};
- return defined $cookie ? "RT_SID_$cookie" : undef;
- }
-
- # Deletes the current session cookie.
- sub delete {
- my ($self) = @_;
- delete $self->{sids}{$s}{$u};
- }
-
- # Adds a Cookie header to an outgoing HTTP request.
- sub add_cookie_header {
- my ($self, $request) = @_;
- my $cookie = $self->cookie();
-
- $request->header(Cookie => $cookie) if defined $cookie;
- }
-
- # Extracts the Set-Cookie header from an HTTP response, and updates
- # session information accordingly.
- sub update {
- my ($self, $response) = @_;
- my $cookie = $response->header("Set-Cookie");
-
- if (defined $cookie && $cookie =~ /^RT_SID_(.[^;,\s]+=[0-9A-Fa-f]+);/) {
- $self->{sids}{$s}{$u} = $1;
- }
- }
-
- # Loads the session cache from the specified file.
- sub load {
- my ($self, $file) = @_;
- $file ||= $self->{file};
- local *F;
-
- open(F, $file) && do {
- $self->{file} = $file;
- my $sids = $self->{sids} = {};
- while (<F>) {
- chomp;
- next if /^$/ || /^#/;
- next unless m#^https?://[^ ]+ \w+ [^;,\s]+=[0-9A-Fa-f]+$#;
- my ($server, $user, $cookie) = split / /, $_;
- $sids->{$server}{$user} = $cookie;
- }
- return 1;
- };
- return 0;
- }
-
- # Writes the current session cache to the specified file.
- sub save {
- my ($self, $file) = shift;
- $file ||= $self->{file};
- local *F;
-
- open(F, ">$file") && do {
- my $sids = $self->{sids};
- foreach my $server (keys %$sids) {
- foreach my $user (keys %{ $sids->{$server} }) {
- my $sid = $sids->{$server}{$user};
- if (defined $sid) {
- print F "$server $user $sid\n";
- }
- }
- }
- close(F);
- chmod 0600, $file;
- return 1;
- };
- return 0;
- }
-
- sub DESTROY {
- my $self = shift;
- $self->save;
- }
-}
-
-# Form handling.
-# --------------
-#
-# Forms are RFC822-style sets of (field, value) specifications with some
-# initial comments and interspersed blank lines allowed for convenience.
-# Sets of forms are separated by --\n (in a cheap parody of MIME).
-#
-# Each form is parsed into an array with four elements: commented text
-# at the start of the form, an array with the order of keys, a hash with
-# key/value pairs, and optional error text if the form syntax was wrong.
-
-# Returns a reference to an array of parsed forms.
-sub Form::parse {
- my $state = 0;
- my @forms = ();
- my @lines = split /\n/, $_[0] if $_[0];
- my ($c, $o, $k, $e) = ("", [], {}, "");
-
- LINE:
- while (@lines) {
- my $line = shift @lines;
-
- next LINE if $line eq '';
-
- if ($line eq '--') {
- # We reached the end of one form. We'll ignore it if it was
- # empty, and store it otherwise, errors and all.
- if ($e || $c || @$o) {
- push @forms, [ $c, $o, $k, $e ];
- $c = ""; $o = []; $k = {}; $e = "";
- }
- $state = 0;
- }
- elsif ($state != -1) {
- if ($state == 0 && $line =~ /^#/) {
- # Read an optional block of comments (only) at the start
- # of the form.
- $state = 1;
- $c = $line;
- while (@lines && $lines[0] =~ /^#/) {
- $c .= "\n".shift @lines;
- }
- $c .= "\n";
- }
- elsif ($state <= 1 && $line =~ /^($field):(?:\s+(.*))?$/) {
- # Read a field: value specification.
- my $f = $1;
- my @v = ($2 || ());
-
- # Read continuation lines, if any.
- while (@lines && ($lines[0] eq '' || $lines[0] =~ /^\s+/)) {
- push @v, shift @lines;
- }
- pop @v while (@v && $v[-1] eq '');
-
- # Strip longest common leading indent from text.
- my $ws = "";
- foreach my $ls (map {/^(\s+)/} @v[1..$#v]) {
- $ws = $ls if (!$ws || length($ls) < length($ws));
- }
- s/^$ws// foreach @v;
-
- push(@$o, $f) unless exists $k->{$f};
- vpush($k, $f, join("\n", @v));
-
- $state = 1;
- }
- elsif ($line !~ /^#/) {
- # We've found a syntax error, so we'll reconstruct the
- # form parsed thus far, and add an error marker. (>>)
- $state = -1;
- $e = Form::compose([[ "", $o, $k, "" ]]);
- $e.= $line =~ /^>>/ ? "$line\n" : ">> $line\n";
- }
- }
- else {
- # We saw a syntax error earlier, so we'll accumulate the
- # contents of this form until the end.
- $e .= "$line\n";
- }
- }
- push(@forms, [ $c, $o, $k, $e ]) if ($e || $c || @$o);
-
- foreach my $l (keys %$k) {
- $k->{$l} = vsplit($k->{$l}) if (ref $k->{$l} eq 'ARRAY');
- }
-
- return \@forms;
-}
-
-# Returns text representing a set of forms.
-sub Form::compose {
- my ($forms) = @_;
- my @text;
-
- foreach my $form (@$forms) {
- my ($c, $o, $k, $e) = @$form;
- my $text = "";
-
- if ($c) {
- $c =~ s/\n*$/\n/;
- $text = "$c\n";
- }
- if ($e) {
- $text .= $e;
- }
- elsif ($o) {
- my @lines;
-
- foreach my $key (@$o) {
- my ($line, $sp);
- my $v = $k->{$key};
- my @values = ref $v eq 'ARRAY' ? @$v : $v;
-
- $sp = " "x(length("$key: "));
- $sp = " "x4 if length($sp) > 16;
-
- foreach $v (@values) {
- if ($v =~ /\n/) {
- $v =~ s/^/$sp/gm;
- $v =~ s/^$sp//;
-
- if ($line) {
- push @lines, "$line\n\n";
- $line = "";
- }
- elsif (@lines && $lines[-1] !~ /\n\n$/) {
- $lines[-1] .= "\n";
- }
- push @lines, "$key: $v\n\n";
- }
- elsif ($line &&
- length($line)+length($v)-rindex($line, "\n") >= 70)
- {
- $line .= ",\n$sp$v";
- }
- else {
- $line = $line ? "$line, $v" : "$key: $v";
- }
- }
-
- $line = "$key:" unless @values;
- if ($line) {
- if ($line =~ /\n/) {
- if (@lines && $lines[-1] !~ /\n\n$/) {
- $lines[-1] .= "\n";
- }
- $line .= "\n";
- }
- push @lines, "$line\n";
- }
- }
-
- $text .= join "", @lines;
- }
- else {
- chomp $text;
- }
- push @text, $text;
- }
-
- return join "\n--\n\n", @text;
-}
-
-# Configuration.
-# --------------
-
-# Returns configuration information from the environment.
-sub config_from_env {
- my %env;
-
- foreach my $k (qw(EXTERNALAUTH DEBUG USER PASSWD SERVER QUERY ORDERBY)) {
-
- if (exists $ENV{"RT$k"}) {
- $env{lc $k} = $ENV{"RT$k"};
- }
- }
-
- return %env;
-}
-
-# Finds a suitable configuration file and returns information from it.
-sub config_from_file {
- my ($rc) = @_;
-
- if ($rc =~ m#^/#) {
- # We'll use an absolute path if we were given one.
- return parse_config_file($rc);
- }
- else {
- # Otherwise we'll use the first file we can find in the current
- # directory, or in one of its (increasingly distant) ancestors.
-
- my @dirs = split /\//, cwd;
- while (@dirs) {
- my $file = join('/', @dirs, $rc);
- if (-r $file) {
- return parse_config_file($file);
- }
-
- # Remove the last directory component each time.
- pop @dirs;
- }
-
- # Still nothing? We'll fall back to some likely defaults.
- for ("$HOME/$rc", "/etc/rt.conf") {
- return parse_config_file($_) if (-r $_);
- }
- }
-
- return ();
-}
-
-# Makes a hash of the specified configuration file.
-sub parse_config_file {
- my %cfg;
- my ($file) = @_;
- local $_; # $_ may be aliased to a constant, from line 1163
-
- open(CFG, $file) && do {
- while (<CFG>) {
- chomp;
- next if (/^#/ || /^\s*$/);
-
- if (/^(externalauth|user|passwd|server|query|orderby|queue)\s+(.*)\s?$/) {
- $cfg{$1} = $2;
- }
- else {
- die "rt: $file:$.: unknown configuration directive.\n";
- }
- }
- };
-
- return %cfg;
-}
-
-# Helper functions.
-# -----------------
-
-sub whine {
- my $sub = (caller(1))[3];
- $sub =~ s/^main:://;
- warn "rt: $sub: @_\n";
- return 0;
-}
-
-sub read_passwd {
- eval 'require Term::ReadKey';
- if ($@) {
- die "No password specified (and Term::ReadKey not installed).\n";
- }
-
- print "Password: ";
- Term::ReadKey::ReadMode('noecho');
- chomp(my $passwd = Term::ReadKey::ReadLine(0));
- Term::ReadKey::ReadMode('restore');
- print "\n";
-
- return $passwd;
-}
-
-sub vi {
- my ($text) = @_;
- my $file = "/tmp/rt.form.$$";
- my $editor = $ENV{EDITOR} || $ENV{VISUAL} || "vi";
-
- local *F;
- local $/ = undef;
-
- open(F, ">$file") || die "$file: $!\n"; print F $text; close(F);
- system($editor, $file) && die "Couldn't run $editor.\n";
- open(F, $file) || die "$file: $!\n"; $text = <F>; close(F);
- unlink($file);
-
- return $text;
-}
-
-# Add a value to a (possibly multi-valued) hash key.
-sub vpush {
- my ($hash, $key, $val) = @_;
- my @val = ref $val eq 'ARRAY' ? @$val : $val;
-
- if (exists $hash->{$key}) {
- unless (ref $hash->{$key} eq 'ARRAY') {
- my @v = $hash->{$key} ne '' ? $hash->{$key} : ();
- $hash->{$key} = \@v;
- }
- push @{ $hash->{$key} }, @val;
- }
- else {
- $hash->{$key} = $val;
- }
-}
-
-# "Normalise" a hash key that's known to be multi-valued.
-sub vsplit {
- my ($val) = @_;
- my ($word, @words);
- my @values = ref $val eq 'ARRAY' ? @$val : $val;
-
- foreach my $line (map {split /\n/} @values) {
- # XXX: This should become a real parser, à la Text::ParseWords.
- $line =~ s/^\s+//;
- $line =~ s/\s+$//;
- my ( $a, $b ) = split /,/, $line, 2;
-
- while ($a) {
- no warnings 'uninitialized';
- if ( $a =~ /^'/ ) {
- my $s = $a;
- while ( $a !~ /'$/ || ( $a !~ /(\\\\)+'$/
- && $a =~ /(\\)+'$/ )) {
- ( $a, $b ) = split /,/, $b, 2;
- $s .= ',' . $a;
- }
- push @words, $s;
- }
- elsif ( $a =~ /^q{/ ) {
- my $s = $a;
- while ( $a !~ /}$/ ) {
- ( $a, $b ) =
- split /,/, $b, 2;
- $s .= ',' . $a;
- }
- $s =~ s/^q{/'/;
- $s =~ s/}/'/;
- push @words, $s;
- }
- else {
- push @words, $a;
- }
- ( $a, $b ) = split /,/, $b, 2;
- }
-
-
- }
-
- return \@words;
-}
-
-# WARN: this code is duplicated in lib/RT/Interface/REST.pm
-# change both functions at once
-sub expand_list {
- my ($list) = @_;
-
- my @elts;
- foreach (split /,/, $list) {
- push @elts, /^(\d+)-(\d+)$/? ($1..$2): $_;
- }
-
- return map $_->[0], # schwartzian transform
- sort {
- defined $a->[1] && defined $b->[1]?
- # both numbers
- $a->[1] <=> $b->[1]
- :!defined $a->[1] && !defined $b->[1]?
- # both letters
- $a->[2] cmp $b->[2]
- # mix, number must be first
- :defined $a->[1]? -1: 1
- }
- map [ $_, (defined( /^(\d+)$/ )? $1: undef), lc($_) ],
- @elts;
-}
-
-sub get_type_argument {
- my $type;
-
- if (@ARGV) {
- $type = shift @ARGV;
- unless ($type =~ /^[A-Za-z0-9_.-]+$/) {
- # We want whine to mention our caller, not us.
- @_ = ("Invalid type '$type' specified.");
- goto &whine;
- }
- }
- else {
- @_ = ("No type argument specified with -t.");
- goto &whine;
- }
-
- $type =~ s/s$//; # "Plural". Ugh.
- return $type;
-}
-
-sub get_var_argument {
- my ($data) = @_;
-
- if (@ARGV) {
- my $kv = shift @ARGV;
- if (my ($k, $v) = $kv =~ /^($field)=(.*)$/) {
- push @{ $data->{$k} }, $v;
- }
- else {
- @_ = ("Invalid variable specification: '$kv'.");
- goto &whine;
- }
- }
- else {
- @_ = ("No variable argument specified with -S.");
- goto &whine;
- }
-}
-
-sub is_object_spec {
- my ($spec, $type) = @_;
-
- $spec =~ s|^(?:$type/)?|$type/| if defined $type;
- return $spec if ($spec =~ m{^$name/(?:$idlist|$labels)(?:/.*)?$}o);
- return 0;
-}
-
-sub suggest_help {
- my ($action, $type, $rv) = @_;
-
- print STDERR "rt: For help, run 'rt help $action'.\n" if defined $action;
- print STDERR "rt: For help, run 'rt help $type'.\n" if defined $type;
- return $rv;
-}
-
-sub str2time {
- # simplified procedure for parsing date, avoid loading Date::Parse
- my %month = (Jan => 0, Feb => 1, Mar => 2, Apr => 3, May => 4, Jun => 5,
- Jul => 6, Aug => 7, Sep => 8, Oct => 9, Nov => 10, Dec => 11);
- $_ = shift;
- my ($mon, $day, $hr, $min, $sec, $yr, $monstr);
- if ( /(\w{3})\s+(\d\d?)\s+(\d\d):(\d\d):(\d\d)\s+(\d{4})/ ) {
- ($monstr, $day, $hr, $min, $sec, $yr) = ($1, $2, $3, $4, $5, $6);
- $mon = $month{$monstr} if exists $month{$monstr};
- } elsif ( /(\d{4})-(\d\d)-(\d\d)\s+(\d\d):(\d\d):(\d\d)/ ) {
- ($yr, $mon, $day, $hr, $min, $sec) = ($1, $2-1, $3, $4, $5, $6);
- }
- if ( $yr and defined $mon and $day and defined $hr and defined $sec ) {
- return timelocal($sec,$min,$hr,$day,$mon,$yr);
- } else {
- print "Unknown date format in parsedate: $_\n";
- return undef;
- }
-}
-
-sub date_diff {
- my ($old, $new) = @_;
- $new = time() if ! $new;
- $old = str2time($old) if $old !~ /^\d+$/;
- $new = str2time($new) if $new !~ /^\d+$/;
- return "???" if ! $old or ! $new;
-
- my %seconds = (min => 60,
- hr => 60*60,
- day => 60*60*24,
- wk => 60*60*24*7,
- mth => 60*60*24*30,
- yr => 60*60*24*365);
-
- my $diff = $new - $old;
- my $what = 'sec';
- my $howmuch = $diff;
- for ( sort {$seconds{$a} <=> $seconds{$b}} keys %seconds) {
- last if $diff < $seconds{$_};
- $what = $_;
- $howmuch = int($diff/$seconds{$_});
- }
- return "$howmuch $what";
-}
-
-sub prettyshow {
- my $forms = shift;
- my ($form) = grep { exists $_->[2]->{Queue} } @$forms;
- my $k = $form->[2];
- # dates are in local time zone
- if ( $k ) {
- print "Date: $k->{Created}\n";
- print "From: $k->{Requestors}\n";
- print "Cc: $k->{Cc}\n" if $k->{Cc};
- print "X-AdminCc: $k->{AdminCc}\n" if $k->{AdminCc};
- print "X-Queue: $k->{Queue}\n";
- print "Subject: [rt #$k->{id}] $k->{Subject}\n\n";
- }
- # dates in these attributes are in GMT and will be converted
- foreach my $form (@$forms) {
- my ($c, $o, $k, $e) = @$form;
- next if ! $k->{id} or exists $k->{Queue};
- if ( exists $k->{Created} ) {
- my ($y,$m,$d,$hh,$mm,$ss) = ($k->{Created} =~ /(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/);
- $m--;
- my $created = localtime(timegm($ss,$mm,$hh,$d,$m,$y));
- if ( exists $k->{Description} ) {
- print "===> $k->{Description} on $created\n";
- }
- }
- print "$k->{Content}\n" if exists $k->{Content} and
- $k->{Content} !~ /to have no content$/ and
- $k->{Type} ne 'EmailRecord';
- print "$k->{Attachments}\n" if exists $k->{Attachments} and
- $k->{Attachments};
- }
-}
-
-sub prettylist {
- my $forms = shift;
- my $heading = "Ticket Owner Queue Age Told Status Requestor Subject\n";
- $heading .= '-' x 80 . "\n";
- my (@open, @me);
- foreach my $form (@$forms) {
- my ($c, $o, $k, $e) = @$form;
- next if ! $k->{id};
- print $heading if $heading;
- $heading = '';
- my $id = $k->{id};
- $id =~ s!^ticket/!!;
- my $owner = $k->{Owner} eq 'Nobody' ? '' : $k->{Owner};
- $owner = substr($owner, 0, 5);
- my $queue = substr($k->{Queue}, 0, 5);
- my $subject = substr($k->{Subject}, 0, 30);
- my $age = date_diff($k->{Created});
- my $told = $k->{Told} eq 'Not set' ? '' : date_diff($k->{Told});
- my $status = substr($k->{Status}, 0, 6);
- my $requestor = substr($k->{Requestors}, 0, 9);
- my $line = sprintf "%6s %5s %5s %6s %6s %-6s %-9s %-30s\n",
- $id, $owner, $queue, $age, $told, $status, $requestor, $subject;
- if ( $k->{Owner} eq 'Nobody' ) {
- push @open, $line;
- } elsif ($k->{Owner} eq $config{user} ) {
- push @me, $line;
- } else {
- print $line;
- }
- }
- print "No matches found\n" if $heading;
- printf "========== my %2d open tickets ==========\n", scalar @me if @me;
- print @me if @me;
- printf "========== %2d unowned tickets ==========\n", scalar @open if @open;
- print @open if @open;
-}
-
-__DATA__
-
-Title: intro
-Title: introduction
-Text:
-
- ** THIS IS AN UNSUPPORTED PREVIEW RELEASE **
- ** PLEASE REPORT BUGS TO rt-bugs@bestpractical.com **
-
- This is a command-line interface to RT 3.0 or newer
-
- It allows you to interact with an RT server over HTTP, and offers an
- interface to RT's functionality that is better-suited to automation
- and integration with other tools.
-
- In general, each invocation of this program should specify an action
- to perform on one or more objects, and any other arguments required
- to complete the desired action.
-
- For more information:
-
- - rt help usage (syntax information)
- - rt help objects (how to specify objects)
- - rt help actions (a list of possible actions)
- - rt help types (a list of object types)
-
- - rt help config (configuration details)
- - rt help examples (a few useful examples)
- - rt help topics (a list of help topics)
-
---
-
-Title: usage
-Title: syntax
-Text:
-
- Syntax:
-
- rt <action> [options] [arguments]
- or
- rt shell
-
- Each invocation of this program must specify an action (e.g. "edit",
- "create"), options to modify behaviour, and other arguments required
- by the specified action. (For example, most actions expect a list of
- numeric object IDs to act upon.)
-
- The details of the syntax and arguments for each action are given by
- "rt help <action>". Some actions may be referred to by more than one
- name ("create" is the same as "new", for example).
-
- You may also call "rt shell", which will give you an 'rt>' prompt at
- which you can issue commands of the form "<action> [options]
- [arguments]". See "rt help shell" for details.
-
- Objects are identified by a type and an ID (which can be a name or a
- number, depending on the type). For some actions, the object type is
- implied (you can only comment on tickets); for others, the user must
- specify it explicitly. See "rt help objects" for details.
-
- In syntax descriptions, mandatory arguments that must be replaced by
- appropriate value are enclosed in <>, and optional arguments are
- indicated by [] (for example, <action> and [options] above).
-
- For more information:
-
- - rt help objects (how to specify objects)
- - rt help actions (a list of actions)
- - rt help types (a list of object types)
- - rt help shell (how to use the shell)
-
---
-
-Title: conf
-Title: config
-Title: configuration
-Text:
-
- This program has two major sources of configuration information: its
- configuration files, and the environment.
-
- The program looks for configuration directives in a file named .rtrc
- (or $RTCONFIG; see below) in the current directory, and then in more
- distant ancestors, until it reaches /. If no suitable configuration
- files are found, it will also check for ~/.rtrc and /etc/rt.conf.
-
- Configuration directives:
-
- The following directives may occur, one per line:
-
- - server <URL> URL to RT server.
- - user <username> RT username.
- - passwd <passwd> RT user's password.
- - query <RT Query> Default RT Query for list action
- - orderby <order> Default RT order for list action
- - queue <queuename> Default RT Queue for list action
- - externalauth <0|1> Use HTTP Basic authentication
- explicitely setting externalauth to 0 inhibits also GSSAPI based
- authentication, if LWP::Authen::Negotiate (and GSSAPI) is installed
-
- Blank and #-commented lines are ignored.
-
- Sample configuration file contents:
-
- server https://rt.somewhere.com/
- # more than one queue can be given (by adding a query expression)
- queue helpdesk or queue=support
- query Status != resolved and Owner=myaccount
-
-
- Environment variables:
-
- The following environment variables override any corresponding
- values defined in configuration files:
-
- - RTUSER
- - RTPASSWD
- - RTEXTERNALAUTH
- - RTSERVER
- - RTDEBUG Numeric debug level. (Set to 3 for full logs.)
- - RTCONFIG Specifies a name other than ".rtrc" for the
- configuration file.
- - RTQUERY Default RT Query for rt list
- - RTORDERBY Default order for rt list
-
---
-
-Title: objects
-Text:
-
- Syntax:
-
- <type>/<id>[/<attributes>]
-
- Every object in RT has a type (e.g. "ticket", "queue") and a numeric
- ID. Some types of objects can also be identified by name (like users
- and queues). Furthermore, objects may have named attributes (such as
- "ticket/1/history").
-
- An object specification is like a path in a virtual filesystem, with
- object types as top-level directories, object IDs as subdirectories,
- and named attributes as further subdirectories.
-
- A comma-separated list of names, numeric IDs, or numeric ranges can
- be used to specify more than one object of the same type. Note that
- the list must be a single argument (i.e., no spaces). For example,
- "user/root,1-3,5,7-10,ams" is a list of ten users; the same list
- can also be written as "user/ams,root,1,2,3,5,7,8-10".
-
- If just a number is given as object specification it will be
- interpreted as ticket/<number>
-
- Examples:
-
- 1 # the same as ticket/1
- ticket/1
- ticket/1/attachments
- ticket/1/attachments/3
- ticket/1/attachments/3/content
- ticket/1-3/links
- ticket/1-3,5-7/history
-
- user/ams
- user/ams/rights
- user/ams,rai,1/rights
-
- For more information:
-
- - rt help <action> (action-specific details)
- - rt help <type> (type-specific details)
-
---
-
-Title: actions
-Title: commands
-Text:
-
- You can currently perform the following actions on all objects:
-
- - list (list objects matching some condition)
- - show (display object details)
- - edit (edit object details)
- - create (create a new object)
-
- Each type may define actions specific to itself; these are listed in
- the help item about that type.
-
- For more information:
-
- - rt help <action> (action-specific details)
- - rt help types (a list of possible types)
-
- The following actions on tickets are also possible:
-
- - comment Add comments to a ticket
- - correspond Add comments to a ticket
- - merge Merge one ticket into another
- - link Link one ticket to another
- - take Take a ticket (steal and untake are possible as well)
-
- For several edit set subcommands that are frequently used abbreviations
- have been introduced. These abbreviations are:
-
- - delete or del delete a ticket (edit set status=deleted)
- - resolve or res resolve a ticket (edit set status=resolved)
- - subject change subject of ticket (edit set subject=string)
- - give give a ticket to somebody (edit set owner=user)
-
---
-
-Title: types
-Text:
-
- You can currently operate on the following types of objects:
-
- - tickets
- - users
- - groups
- - queues
-
- For more information:
-
- - rt help <type> (type-specific details)
- - rt help objects (how to specify objects)
- - rt help actions (a list of possible actions)
-
---
-
-Title: ticket
-Text:
-
- Tickets are identified by a numeric ID.
-
- The following generic operations may be performed upon tickets:
-
- - list
- - show
- - edit
- - create
-
- In addition, the following ticket-specific actions exist:
-
- - link
- - merge
- - comment
- - correspond
- - take
- - steal
- - untake
- - give
- - resolve
- - delete
- - subject
-
- Attributes:
-
- The following attributes can be used with "rt show" or "rt edit"
- to retrieve or edit other information associated with tickets:
-
- links A ticket's relationships with others.
- history All of a ticket's transactions.
- history/type/<type> Only a particular type of transaction.
- history/id/<id> Only the transaction of the specified id.
- attachments A list of attachments.
- attachments/<id> The metadata for an individual attachment.
- attachments/<id>/content The content of an individual attachment.
-
---
-
-Title: user
-Title: group
-Text:
-
- Users and groups are identified by name or numeric ID.
-
- The following generic operations may be performed upon them:
-
- - list
- - show
- - edit
- - create
-
- In addition, the following type-specific actions exist:
-
- - grant
- - revoke
-
- Attributes:
-
- The following attributes can be used with "rt show" or "rt edit"
- to retrieve or edit other information associated with users and
- groups:
-
- rights Global rights granted to this user.
- rights/<queue> Queue rights for this user.
-
---
-
-Title: queue
-Text:
-
- Queues are identified by name or numeric ID.
-
- Currently, they can be subjected to the following actions:
-
- - show
- - edit
- - create
-
---
-
-Title: subject
-Text:
-
- Syntax:
-
- rt subject <id> <new subject text>
-
- Change the subject of a ticket whose ticket id is given.
-
---
-
-Title: give
-Text:
-
- Syntax:
-
- rt give <id> <accountname>
-
- Give a ticket whose ticket id is given to another user.
-
---
-
-Title: steal
-Text:
-
- rt steal <id>
-
- Steal a ticket whose ticket id is given, i.e. set the owner to myself.
-
---
-
-Title: take
-Text:
-
- Syntax:
-
- rt take <id>
-
- Take a ticket whose ticket id is given, i.e. set the owner to myself.
-
---
-
-Title: untake
-Text:
-
- Syntax:
-
- rt untake <id>
-
- Untake a ticket whose ticket id is given, i.e. set the owner to Nobody.
-
---
-
-Title: resolve
-Title: res
-Text:
-
- Syntax:
-
- rt resolve <id>
-
- Resolves a ticket whose ticket id is given.
-
---
-
-Title: delete
-Title: del
-Text:
-
- Syntax:
-
- rt delete <id>
-
- Deletes a ticket whose ticket id is given.
-
---
-
-Title: logout
-Text:
-
- Syntax:
-
- rt logout
-
- Terminates the currently established login session. You will need to
- provide authentication credentials before you can continue using the
- server. (See "rt help config" for details about authentication.)
-
---
-
-Title: ls
-Title: list
-Title: search
-Text:
-
- Syntax:
-
- rt <ls|list|search> [options] "query string"
-
- Displays a list of objects matching the specified conditions.
- ("ls", "list", and "search" are synonyms.)
-
- Conditions are expressed in the SQL-like syntax used internally by
- RT3. (For more information, see "rt help query".) The query string
- must be supplied as one argument.
-
- (Right now, the server doesn't support listing anything but tickets.
- Other types will be supported in future; this client will be able to
- take advantage of that support without any changes.)
-
- Options:
-
- The following options control how much information is displayed
- about each matching object:
-
- -i Numeric IDs only. (Useful for |rt edit -; see examples.)
- -s Short description.
- -l Longer description.
- -f <field[s] Display only the fields listed and the ticket id
-
- In addition,
-
- -o +/-<field> Orders the returned list by the specified field.
- -r reversed order (useful if a default was given)
- -q queue[s] restricts the query to the queue[s] given
- multiple queues are separated by comma
- -S var=val Submits the specified variable with the request.
- -t type Specifies the type of object to look for. (The
- default is "ticket".)
-
- Examples:
-
- rt ls "Priority > 5 and Status=new"
- rt ls -o +Subject "Priority > 5 and Status=new"
- rt ls -o -Created "Priority > 5 and Status=new"
- rt ls -i "Priority > 5"|rt edit - set status=resolved
- rt ls -t ticket "Subject like '[PATCH]%'"
- rt ls -q systems
- rt ls -f owner,subject
-
---
-
-Title: show
-Text:
-
- Syntax:
-
- rt show [options] <object-ids>
-
- Displays details of the specified objects.
-
- For some types, object information is further classified into named
- attributes (for example, "1-3/links" is a valid ticket specification
- that refers to the links for tickets 1-3). Consult "rt help <type>"
- and "rt help objects" for further details.
-
- If only a number is given it will be interpreted as the objects
- ticket/number and ticket/number/history
-
- This command writes a set of forms representing the requested object
- data to STDOUT.
-
- Options:
-
- The following options control how much information is displayed
- about each matching object:
-
- Without any formatting options prettyprinted output is generated.
- Giving any of the two options below reverts to raw output.
- -s Short description (history and attachments only).
- -l Longer description (history and attachments only).
-
- In addition,
- - Read IDs from STDIN instead of the command-line.
- -t type Specifies object type.
- -f a,b,c Restrict the display to the specified fields.
- -S var=val Submits the specified variable with the request.
-
- Examples:
-
- rt show -t ticket -f id,subject,status 1-3
- rt show ticket/3/attachments/29
- rt show ticket/3/attachments/29/content
- rt show ticket/1-3/links
- rt show ticket/3/history
- rt show -l ticket/3/history
- rt show -t user 2
- rt show 2
-
---
-
-Title: new
-Title: edit
-Title: create
-Text:
-
- Syntax:
-
- rt edit [options] <object-ids> set field=value [field=value] ...
- add field=value [field=value] ...
- del field=value [field=value] ...
-
- Edits information corresponding to the specified objects.
-
- A purely numeric object id nnn is translated into ticket/nnn
-
- If, instead of "edit", an action of "new" or "create" is specified,
- then a new object is created. In this case, no numeric object IDs
- may be specified, but the syntax and behaviour remain otherwise
- unchanged.
-
- This command typically starts an editor to allow you to edit object
- data in a form for submission. If you specified enough information
- on the command-line, however, it will make the submission directly.
-
- The command line may specify field-values in three different ways.
- "set" sets the named field to the given value, "add" adds a value
- to a multi-valued field, and "del" deletes the corresponding value.
- Each "field=value" specification must be given as a single argument.
-
- For some types, object information is further classified into named
- attributes (for example, "1-3/links" is a valid ticket specification
- that refers to the links for tickets 1-3). These attributes may also
- be edited. Consult "rt help <type>" and "rt help object" for further
- details.
-
- Options:
-
- - Read numeric IDs from STDIN instead of the command-line.
- (Useful with rt ls ... | rt edit -; see examples below.)
- -i Read a completed form from STDIN before submitting.
- -o Dump the completed form to STDOUT instead of submitting.
- -e Allows you to edit the form even if the command-line has
- enough information to make a submission directly.
- -S var=val
- Submits the specified variable with the request.
- -t type Specifies object type.
-
- Examples:
-
- # Interactive (starts $EDITOR with a form).
- rt edit ticket/3
- rt create -t ticket
-
- # Non-interactive.
- rt edit ticket/1-3 add cc=foo@example.com set priority=3 due=tomorrow
- rt ls -t tickets -i 'Priority > 5' | rt edit - set status=resolved
- rt edit ticket/4 set priority=3 owner=bar@example.com \
- add cc=foo@example.com bcc=quux@example.net
- rt create -t ticket set subject='new ticket' priority=10 \
- add cc=foo@example.com
-
---
-
-Title: comment
-Title: correspond
-Text:
-
- Syntax:
-
- rt <comment|correspond> [options] <ticket-id>
-
- Adds a comment (or correspondence) to the specified ticket (the only
- difference being that comments aren't sent to the requestors.)
-
- This command will typically start an editor and allow you to type a
- comment into a form. If, however, you specified all the necessary
- information on the command line, it submits the comment directly.
-
- (See "rt help forms" for more information about forms.)
-
- Options:
-
- -m <text> Specify comment text.
- -a <file> Attach a file to the comment. (May be used more
- than once to attach multiple files.)
- -c <addrs> A comma-separated list of Cc addresses.
- -b <addrs> A comma-separated list of Bcc addresses.
- -w <time> Specify the time spent working on this ticket.
- -e Starts an editor before the submission, even if
- arguments from the command line were sufficient.
-
- Examples:
-
- rt comment -m 'Not worth fixing.' -a stddisclaimer.h 23
-
---
-
-Title: merge
-Text:
-
- Syntax:
-
- rt merge <from-id> <to-id>
-
- Merges the first ticket specified into the second ticket specified.
-
---
-
-Title: link
-Text:
-
- Syntax:
-
- rt link [-d] <id-A> <link> <id-B>
-
- Creates (or, with -d, deletes) a link between the specified tickets.
- The link can (irrespective of case) be any of:
-
- DependsOn/DependedOnBy: A depends upon B (or vice versa).
- RefersTo/ReferredToBy: A refers to B (or vice versa).
- MemberOf/HasMember: A is a member of B (or vice versa).
-
- To view a ticket's links, use "rt show ticket/3/links". (See
- "rt help ticket" and "rt help show".)
-
- Options:
-
- -d Deletes the specified link.
-
- Examples:
-
- rt link 2 dependson 3
- rt link -d 4 referredtoby 6 # 6 no longer refers to 4
-
---
-
-Title: grant
-Title: revoke
-Text:
-
---
-
-Title: query
-Text:
-
- RT3 uses an SQL-like syntax to specify object selection constraints.
- See the <RT:...> documentation for details.
-
- (XXX: I'm going to have to write it, aren't I?)
-
- Until it exists here a short description of important constructs:
-
- The two simple forms of query expressions are the constructs
- Attribute like Value and
- Attribute = Value or Attribute != Value
-
- Whether attributes can be matched using like or using = is built into RT.
- The attributes id, Queue, Owner Priority and Status require the = or !=
- tests.
-
- If Value is a string it must be quoted and may contain the wildcard
- character %. If the string does not contain white space, the quoting
- may however be omitted, it will be added automatically when parsing
- the input.
-
- Simple query expressions can be combined using and, or and parentheses
- can be used to group expressions.
-
- As a special case a standalone string (which would not form a correct
- query) is transformed into (Owner='string' or Requestor like 'string%')
- and added to the default query, i.e. the query is narrowed down.
-
- If no Queue=name clause is contained in the query, a default clause
- Queue=$config{queue} is added.
-
- Examples:
- Status!='resolved' and Status!='rejected'
- (Owner='myaccount' or Requestor like 'myaccount%') and Status!='resolved'
-
---
-
-Title: form
-Title: forms
-Text:
-
- This program uses RFC822 header-style forms to represent object data
- in a form that's suitable for processing both by humans and scripts.
-
- A form is a set of (field, value) specifications, with some initial
- commented text and interspersed blank lines allowed for convenience.
- Field names may appear more than once in a form; a comma-separated
- list of multiple field values may also be specified directly.
-
- Field values can be wrapped as in RFC822, with leading whitespace.
- The longest sequence of leading whitespace common to all the lines
- is removed (preserving further indentation). There is no limit on
- the length of a value.
-
- Multiple forms are separated by a line containing only "--\n".
-
- (XXX: A more detailed specification will be provided soon. For now,
- the server-side syntax checking will suffice.)
-
---
-
-Title: topics
-Text:
-
- Syntax:
-
- rt help <topic>
-
- Get help on any of the following subjects:
-
- - tickets, users, groups, queues.
- - show, edit, ls/list/search, new/create.
-
- - query (search query syntax)
- - forms (form specification)
-
- - objects (how to specify objects)
- - types (a list of object types)
- - actions/commands (a list of actions)
- - usage/syntax (syntax details)
- - conf/config/configuration (configuration details)
- - examples (a few useful examples)
-
---
-
-Title: example
-Title: examples
-Text:
-
- some useful examples
-
- All the following list requests will be restricted to the default queue.
- That can be changed by adding the option -q queuename
-
- List all tickets that are not rejected/resolved
- rt ls
- List all tickets that are new and do not have an owner
- rt ls "status=new and owner=nobody"
- List all tickets which I have sent or of which I am the owner
- rt ls myaccount
- List all attributes for the ticket 6977 (ls -l instead of ls)
- rt ls -l 6977
- Show the content of ticket 6977
- rt show 6977
- Show all attributes in the ticket and in the history of the ticket
- rt show -l 6977
- Comment a ticket (mail is sent to all queue watchers, i.e. AdminCc's)
- rt comment 6977
- This will open an editor and lets you add text (attribute Text:)
- Other attributes may be changed as well, but usually don't do that.
- Correspond a ticket (like comment, but mail is also sent to requestors)
- rt correspond 6977
- Edit a ticket (generic change, interactive using the editor)
- rt edit 6977
- Change the owner of a ticket non interactively
- rt edit 6977 set owner=myaccount
- or
- rt give 6977 account
- or
- rt take 6977
- Change the status of a ticket
- rt edit 6977 set status=resolved
- or
- rt resolve 6977
- Change the status of all tickets I own to resolved !!!
- rt ls -i owner=myaccount | rt edit - set status=resolved
-
---
-
-Title: shell
-Text:
-
- Syntax:
-
- rt shell
-
- Opens an interactive shell, at which you can issue commands of
- the form "<action> [options] [arguments]".
-
- To exit the shell, type "quit" or "exit".
-
- Commands can be given at the shell in the same form as they would
- be given at the command line without the leading 'rt' invocation.
-
- Example:
- $ rt shell
- rt> create -t ticket set subject='new' add cc=foo@example.com
- # Ticket 8 created.
- rt> quit
- $
-
---
-
-Title: take
-Title: untake
-Title: steal
-Text:
-
- Syntax:
-
- rt <take|untake|steal> <ticket-id>
-
- Sets the owner of the specified ticket to the current user,
- assuming said user has the bits to do so, or releases the
- ticket.
-
- 'Take' is used on tickets which are not currently owned
- (Owner: Nobody), 'steal' is used on tickets which *are*
- currently owned, and 'untake' is used to "release" a ticket
- (reset its Owner to Nobody). 'Take' cannot be used on
- tickets which are currently owned.
-
- Example:
- alice$ rt create -t ticket set subject="New ticket"
- # Ticket 7 created.
- alice$ rt take 7
- # Owner changed from Nobody to alice
- alice$ su bob
- bob$ rt steal 7
- # Owner changed from alice to bob
- bob$ rt untake 7
- # Owner changed from bob to Nobody
-
---
-
-Title: quit
-Title: exit
-Text:
-
- Use "quit" or "exit" to leave the shell. Only valid within shell
- mode.
-
- Example:
- $ rt shell
- rt> quit
- $
diff --git a/rt/bin/rt-commit-handler b/rt/bin/rt-commit-handler
index 29e443ebd..bf23a6c0b 100644
--- a/rt/bin/rt-commit-handler
+++ b/rt/bin/rt-commit-handler
@@ -26,7 +26,7 @@
# {{{ Docs
# -*-Perl-*-
#
-#ident "@(#)ccvs/contrib:$Name: $:$Id: rt-commit-handler,v 1.1 2003-07-15 13:16:15 ivan Exp $"
+#ident "@(#)ccvs/contrib:$Name: $:$Id: rt-commit-handler,v 1.2 2007-08-01 22:20:32 ivan Exp $"
#
# Perl filter to handle the log messages from the checkin of files in multiple
# directories. This script will group the lists of files by log message, and
diff --git a/rt/bin/rt-commit-handler.in b/rt/bin/rt-commit-handler.in
deleted file mode 100644
index 02b01abff..000000000
--- a/rt/bin/rt-commit-handler.in
+++ /dev/null
@@ -1,846 +0,0 @@
-#!@PERL@ -w
-# BEGIN LICENSE BLOCK
-#
-# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-
-# {{{ Docs
-# -*-Perl-*-
-#
-#ident "@(#)ccvs/contrib:$Name: $:$Id: rt-commit-handler.in,v 1.1 2003-07-15 13:16:15 ivan Exp $"
-#
-# Perl filter to handle the log messages from the checkin of files in multiple
-# directories. This script will group the lists of files by log message, and
-# send one piece of mail per unique message, no matter how many files are
-# committed.
-
-=head1 NAME rt-commit-handler
-
-=head1 USAGE
-
-
-
-=head2 Regular use
-
-Stick the following in in CVSROOT/commitinfo
-
- ALL @RT_BIN_PATH@/rt-commit-handler --record-last-dir
-
-Stick the following in CVSROOT/loginfo
-
- ALL @RT_BIN_PATH@/rt-commit-handler --cvs-root /pathtocvs/root --rt %{Vvts}
-
-=head2 Invocation (advanced use)
-
-rt-commit-handler --cvs-root /path/to/cvs/root [-d] [-D] [-r] [-M module] \
- [[-m mailto] ...] [[-R replyto] ...] [-f logfile]
-
-
- -d - turn on debugging
- -m mailto - send mail to "mailto" (multiple)
- -R replyto - set the "Reply-To:" to "replyto" (multiple)
- -M modulename - set module name to "modulename"
- -f logfile - write commit messages to logfile too
- -D - generate diff commands
- --rt - invoke RT commit handler
- --cvs-root - specify your CVS root
-
- --record-last-dir - Record the last directory with changes in
- pre-commit (commitinfo) mode
-
-
-=cut
-
-# }}}
-
-use strict;
-use Carp;
-use Getopt::Long;
-use Text::Wrap;
-use Digest::MD5;
-use MIME::Entity;
-
-use lib ("@RT_LIB_PATH@", "@LOCAL_LIB_PATH@");
-
-use RT::Interface::CLI qw(CleanEnv GetCurrentUser GetMessageContent loc);
-
-use vars
- qw(@MAILER $TMPDIR $FILE_PREFIX $LASTDIR_FILE $HASH_FILE $VERSION_FILE $MESSAGE_FILE $MAIL_FILE $DEBUG $MAILTO $REPLYTO $id $MODULE_NAME
- $LOGIN $COMMITLOG $CVS_ROOT $RT_HANDLER);
-
-#Clean out all the nasties from the environment
-CleanEnv();
-
-#Load etc/config.pm and drop privs
-RT::LoadConfig();
-
-#Drop setgid permissions
-RT::DropSetGIDPermissions();
-
-# {{{ Variable setup
-$TMPDIR = '/tmp';
-$FILE_PREFIX = $TMPDIR . '/#cvs.';
-
-# The root of your CVS install. we should get this from some smarter place.
-# It needs a trailing /
-
-$LASTDIR_FILE = $FILE_PREFIX . "lastdir";
-$HASH_FILE = $FILE_PREFIX . "hash";
-$VERSION_FILE = $FILE_PREFIX . "version";
-$MESSAGE_FILE = $FILE_PREFIX . "message";
-$MAIL_FILE = $FILE_PREFIX . "mail";
-
-$DEBUG = 0;
-$RT_HANDLER = 1;
-
-$MAILTO = '';
-
-my @files = ();
-my (@log_lines);
-my $do_diff = 0;
-my $id = getpgrp(); # note, you *must* use a shell which does setpgrp()
-$LOGIN = getpwuid($<);
-
-# }}}
-
-die "User could not be found" unless ($LOGIN);
-
-# {{{ parse command line arguments (file list is seen as one arg)
-#
-while ( my $arg = shift @ARGV ) {
-
- if ( $arg eq '-d' ) {
- $DEBUG = 1;
- warn "Debug turned on...\n";
- }
- elsif ( $arg =~ /^--record-last-dir$/i ) {
- record_last_dir( $id, $ARGV[0] );
- exit(0);
- }
- elsif ( $arg eq '-m' ) {
- $MAILTO .= ", " if $MAILTO;
- $MAILTO .= shift @ARGV;
- }
- elsif ( $arg eq '--rt' ) {
- $RT_HANDLER = 1;
- }
- elsif ( $arg eq '-R' ) {
- $REPLYTO .= ", " if $REPLYTO;
- $REPLYTO .= shift @ARGV;
- }
- elsif ( $arg eq '-M' ) {
- die ("too many '-M' args\n") if $MODULE_NAME;
- $MODULE_NAME = shift @ARGV;
- }
- elsif ( $arg eq '--cvs-root' ) {
- $CVS_ROOT = shift @ARGV;
- $CVS_ROOT .= "/" unless ( $CVS_ROOT =~ /\/$/ );
- }
- elsif ( $arg eq '-f' ) {
- die ("too many '-f' args\n") if $COMMITLOG;
- $COMMITLOG = shift @ARGV;
-
- # This is a disgusting hack to untaint $COMMITLOG if we're running from
- # setgid cvs.
- $COMMITLOG = untaint($COMMITLOG);
- }
- elsif ( $arg eq '-D' ) {
- $do_diff = 1;
- }
- else {
- @files = split ( ' ', $arg );
- last;
- }
-}
-
-# }}}
-
-$REPLYTO = $LOGIN unless ($REPLYTO);
-
-# for now, the first "file" is the repository directory being committed,
-# relative to the $CVSROOT location
-#
-my $dir = shift @files;
-
-# XXX there are some ugly assumptions in here about module names and
-# XXX directories relative to the $CVSROOT location -- really should
-# XXX read $CVSROOT/CVSROOT/modules, but that's not so easy to do, since
-# XXX we have to parse it backwards.
-#
-# XXX For now we set the `module' name to the top-level directory name.
-#
-unless ($MODULE_NAME) {
- ($MODULE_NAME) = split ( '/', $dir, 2 );
-}
-
-if ($DEBUG) {
- warn "module - ", $MODULE_NAME, "\n";
- warn "dir - ", $dir, "\n";
- warn "files - ", join ( " ", @files ), "\n";
- warn "id - ", $id, "\n";
-}
-
-# {{{ Check for a new directory or an import command.
-
-#
-# files[0] - "-"
-# files[1] - "New"
-# files[2] - "directory"
-#
-# files[0] - "-"
-# files[1] - "Imported"
-# files[2] - "sources"
-#
-if ( $files[0] eq "-" ) {
-
- #we just don't care about New Directory notes
- unless ( $files[1] eq "New" && $files[2] eq "directory" ) {
-
- my @text = ();
-
- push @text, build_header();
- push @text, "";
-
- while ( my $line = <STDIN> ) {
- chop $line; # Drop the newline
- push @text, $line;
- }
-
- append_logfile( $COMMITLOG, @text ) if ($COMMITLOG);
-
- mail_notification( $id, @text );
- }
-
- exit 0;
-}
-
-# }}}
-
-# {{{ Collect just the log message from stdin.
-#
-
-while ( my $line = <STDIN> ) {
- chop $line; # strip the newline
- last if ( $line =~ /^Log Message:$/ );
-}
-while ( my $line = <STDIN> ) {
- chop $line; # strip the newline
- $line =~ s/\s+$//; # strip trailing white space
- push @log_lines, $line;
-}
-
-my $md5 = Digest::MD5->new();
-foreach my $line (@log_lines) {
- $md5->add( $line . "\n" );
-}
-my $hash = $md5->hexdigest();
-
-warn "hash = $hash\n" if ($DEBUG);
-
-if ( !-e "$MESSAGE_FILE.$id.$hash" ) {
- append_logfile( "$HASH_FILE.$id", $hash );
- write_file( "$MESSAGE_FILE.$id.$hash", @log_lines );
-}
-
-# }}}
-
-# Spit out the information gathered in this pass.
-
-append_logfile( "$VERSION_FILE.$id.$hash", $dir . '/', @files );
-
-# {{{ Check whether this is the last directory. If not, quit.
-
-warn "Checking current dir against last dir $LASTDIR_FILE.$id\n" if ($DEBUG);
-
-my @last_dir = read_file("$LASTDIR_FILE.$id");
-
-unless ($CVS_ROOT) {
- die "No cvs root specified with --cvs-root. Can't continue.";
-}
-
-if ( $last_dir[0] ne $CVS_ROOT . $dir ) {
- warn "Current directory $CVS_ROOT$dir is not last directory $last_dir[0].\n"
- if ($DEBUG);
- exit 0;
-}
-
-# }}}
-
-# {{{ End Of Commits!
-#
-
-# This is it. The commits are all finished. Lump everything together
-# into a single message, fire a copy off to the mailing list, and drop
-# it on the end of the Changes file.
-#
-
-#
-# Produce the final compilation of the log messages
-#
-
-my @hashes = read_file("$HASH_FILE.$id");
-my (@text);
-
-push @text, build_header();
-push @text, "";
-
-my ( @added_files, @modified_files, @removed_files );
-
-foreach my $hash (@hashes) {
-
- # In case we're running setgid, make sure the hash file hasn't been hacked.
- $hash =~ m/([a-z0-9]*)/ || die "*** Hacking attempt detected\n";
- $hash = $1;
-
- my @files = read_file("$VERSION_FILE.$id.$hash");
- my @log_lines = read_file("$MESSAGE_FILE.$id.$hash");
-
- my $working_on_dir; # gets set as we iterate through the files.
- foreach my $file (@files) {
-
- #If we've entered a new directory, make a note of that and remove the trailing /
-
- if ( $file =~ s'\/$'' ) {
- $working_on_dir = $file;
- next;
- }
-
- my @file_entry = ( split ( ',', $file, 4 ), $working_on_dir );
-
- # file_entry looks like ths:
-
- # 0 1 2 3 4
- # Old rev : new rev : tag: file :directory
- my $entry = {};
- $entry->{'old'} = $file_entry[0];
- $entry->{'new'} = $file_entry[1];
- $entry->{'tag'} = $file_entry[2];
- $entry->{'file'} = $file_entry[3];
- $entry->{'dir'} = $file_entry[4];
-
- if ( $file_entry[0] eq 'NONE' ) {
- $entry->{'old'} = '0';
- push @added_files, $entry;
- }
- elsif ( $file_entry[1] eq 'NONE' ) {
- $entry->{'new'} = '0';
- push @removed_files, $entry;
- }
- else {
- push @modified_files, $entry;
- }
- }
-}
-
-# }}}
-
-# {{{ start building up the body
-
-# Strip leading and trailing blank lines from the log message. Also
-# compress multiple blank lines in the body of the message down to a
-# single blank line.
-#
-
-my $blank = 1;
-@log_lines = map {
- my $wasblank = $blank;
- $blank = $_ eq '';
- $blank && $wasblank ? () : $_;
-} @log_lines;
-
-pop @log_lines if $blank;
-
-@modified_files = order_and_summarize_diffs(@modified_files);
-@added_files = order_and_summarize_diffs(@added_files);
-@removed_files = order_and_summarize_diffs(@removed_files);
-
-push @text, "Modified Files:", format_lists(@modified_files)
- if (@modified_files);
-
-push @text, "Added Files:", format_lists(@added_files) if (@added_files);
-
-push @text, "Removed Files:", format_lists(@removed_files) if (@removed_files);
-
-push @text, "", "Log Message", @log_lines if (@log_lines);
-
-push @text, "";
-
-if ($RT_HANDLER) {
- rt_handler(
- @log_lines, "\n",
- loc("To generate a diff of this commit:\n"), "\n",
- format_diffs( @modified_files, @added_files, @removed_files )
- );
-}
-
-if ($COMMITLOG) {
- append_logfile( $COMMITLOG, @text );
-}
-
-if ($do_diff) {
- push @text, "";
- push @text, loc("To generate a diff of this commit:");
- push @text, format_diffs( @modified_files, @added_files, @removed_files );
- push @text, "";
-}
-
-# }}}
-
-# {{{ Mail out the notification.
-
-mail_notification( $id, @text );
-
-# }}}
-
-# {{{ clean up
-
-unless ($DEBUG) {
- $hash = untaint($hash);
- $id = untaint($id);
- unlink "$VERSION_FILE.$id.$hash";
- unlink "$MESSAGE_FILE.$id.$hash";
- unlink "$MAIL_FILE.$id";
- unlink "$LASTDIR_FILE.$id";
- unlink "$HASH_FILE.$id";
-}
-
-# }}}
-
-exit 0;
-
-# {{{ Subroutines
-#
-
-# {{{ append_logfile
-sub append_logfile {
- my $filename = shift;
- my (@lines) = @_;
-
- $filename = untaint($filename);
-
- open( FILE, ">>$filename" )
- || die ("Cannot open file $filename for append.\n");
- foreach my $line (@lines) {
- print FILE $line . "\n";
- }
- close(FILE);
-}
-
-# }}}
-
-# {{{ write_file
-sub write_file {
- my $filename = shift;
- my (@lines) = @_;
-
- $filename = untaint($filename);
-
- open( FILE, ">$filename" )
- || die ("Cannot open file $filename for write.\n");
- foreach my $line (@lines) {
- print FILE $line . "\n";
- }
- close(FILE);
-}
-
-# }}}
-
-# {{{ read_file
-sub read_file {
- my $filename = shift;
- my (@lines);
-
- open( FILE, "<$filename" )
- || die ("Cannot open file $filename for read.\n");
- while ( my $line = <FILE> ) {
- chop $line;
- push @lines, $line;
- }
- close(FILE);
-
- return (@lines);
-}
-
-# }}}
-
-# {{{ sub format_lists
-
-sub format_lists {
- my @items = (@_);
-
- my $files = "";
- map {
- $_->{'files'} && ( $files .= ' ' . join ( ' ', @{ $_->{'files'} } ) );
- } @items;
-
- my @lines = wrap( "\t", "\t\t", $files );
- return (@lines);
-
-}
-
-# }}}
-
-# {{{ sub format_diffs
-
-sub format_diffs {
- my @items = (@_);
-
- my @lines;
- foreach my $item (@items) {
- next unless ( $item->{'files'} );
- push ( @lines,
- "cvs diff -r"
- . $item->{'old'} . " -r"
- . $item->{'new'} . " "
- . join ( " ", @{ $item->{'files'} } ) . "\n" );
-
- }
-
- @lines = fill( "\t", "\t\t", @lines );
-
- return (@lines);
-}
-
-# }}}
-
-# {{{ sub order_and_summarize_diffs {
-
-# takes an array of file items
-# returns a sorted array of fileset items, which are like file items, except they can have an array of files, rather than
-# a singleton file.
-
-sub order_and_summarize_diffs {
-
- my @files = (@_);
-
- # Sort by tag, dir, file.
- @files = sort {
- $a->{'tag'} cmp $b->{'tag'}
- || $a->{'dir'} cmp $b->{'dir'}
- || $a->{'file'} cmp $b->{'file'};
- } @files;
-
- # Combine adjacent rows that are the same modulo the file name.
-
- my @items = (undef);
-
- foreach my $file (@files) {
- if ( $#items == -1 #if it's empty
- || ( !defined $items[-1]->{'old'}
- || $items[-1]->{'old'} ne $file->{'old'} )
- || ( !defined $items[-1]->{'new'}
- || $items[-1]->{'new'} ne $file->{'new'} )
- || ( !defined $items[-1]->{'tag'}
- || $items[-1]->{'tag'} ne $file->{'tag'} ) )
- {
-
- push ( @items, $file );
- }
- push ( @{ $items[-1]->{'files'} },
- $file->{'dir'} . "/" . $file->{'file'} );
- }
-
- return (@items);
-}
-
-# }}}
-
-# {{{ build_header
-
-sub build_header {
- my $now = gmtime;
- my $header =
- sprintf( "Module Name:\t%s\nCommitted By:\t%s\nDate:\t\t%s %s %s",
- $MODULE_NAME, $LOGIN, substr( $now, 0, 19 ), "UTC",
- substr( $now, 20, 4 ) );
- return ($header);
-}
-
-# }}}
-
-# {{{ mail_notification
-sub mail_notification {
- my $id = shift;
- my (@text) = @_;
- write_file( "$MAIL_FILE.$id", "From: " . $LOGIN,
- "Subject: CVS commit: " . $MODULE_NAME, "To: " . $MAILTO,
- "Reply-To: " . $REPLYTO, "", "", @text );
-
- my $entity = MIME::Entity->build(
- From => $LOGIN,
- To => $MAILTO,
- Subject => "CVS commit: " . $MODULE_NAME,
- 'Reply-To' => $REPLYTO,
- Data => join ( "\n", @text )
- );
- if ( $RT::MailCommand eq 'sendmailpipe' ) {
- open( MAIL, "|$RT::SendmailPath $RT::SendmailArguments" )
- || die "Couldn't send mail: " . $@ . "\n";
- print MAIL $entity->as_string;
- close(MAIL);
- }
- else {
- $entity->send( $RT::MailCommand, $RT::MailParams );
- }
-
-}
-
-# }}}
-
-# {{{ sub record_last_dir
-
-sub record_last_dir {
- my $id = shift;
- my $dir = shift;
-
- # make a note of this directory. later, we'll use this to
- # figure out if we've gone through the whole commit,
- # for something that is a bad mockery of attomic commits.
-
- warn "about to write $dir to $LASTDIR_FILE.$id" if ($DEBUG);
-
- write_file( "$LASTDIR_FILE.$id", $dir );
-}
-
-# }}}
-
-# {{{ Get the RT stuff set up
-
-# {{{ sub rt_handler
-
-sub rt_handler {
- my (@LogMessage) = (@_);
-
- #Connect to the database and get RT::SystemUser and RT::Nobody loaded
- RT::Init;
-
- require RT::Ticket;
-
- #Get the current user all loaded
- my $CurrentUser = GetCurrentUser();
-
- if ( !$CurrentUser->Id ) {
- print
-loc("No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\n");
- return;
- }
-
- my (@commands) = find_commands( \@LogMessage );
-
- my ( @tickets, @errors );
-
- # Get the list of tickets we're working with out of commands
- grep { $_ =~ /^RT-Ticket:\s*(.*?)$/i && push ( @tickets, $1 ) } @commands;
-
- my $message = new MIME::Entity;
- $message->build(
- From => $CurrentUser->EmailAddress,
- Subject => 'CVS Commit',
- Data => \@LogMessage
- );
-
- # {{{ comment or correspond, as needed
-
- foreach my $ticket (@tickets) {
- my $TicketObj = RT::Ticket->new($CurrentUser);
- $TicketObj->Load($ticket);
- my ( $id, $msg );
- unless ( $TicketObj->Id ) {
- push ( @errors,
-"Couldn't load ticket #$ticket. Not adding commit log to ticket history.\n"
- );
- }
-
- if ( $LogMessage[0] =~ /^(comment|private)$/ ) {
- ( $id, $msg ) = $TicketObj->Comment( MIMEObj => $message );
-
- }
- else {
- ( $id, $msg ) = $TicketObj->Correspond( MIMEObj => $message );
- }
-
- push ( @errors, ">> Log message",
- "Ticket #" . $TicketObj->Id . ": " . $msg );
-
- }
-
- # }}}
-
- my ($reply) = ActOnPseudoHeaders( $CurrentUser, @commands );
- print "$reply\n" if ($reply);
- print join ( "\n", @errors );
- print "\n";
-
-}
-
-# }}}
-
-# {{{ sub find_commands
-
-sub find_commands {
- my $lines = shift;
- my (@pseudoheaders);
-
- while ( my $line = shift @{$lines} ) {
- next if $line =~ /^\s*?$/;
- if ( $line =~ /^RT-/i ) {
-
- push ( @pseudoheaders, $line );
- }
-
- #If we find a line that's not a command, get out.
- else {
- unshift ( @{$lines}, $line );
- last;
- }
- }
-
- return (@pseudoheaders);
-
-}
-
-# }}}
-
-# {{{ sub ActOnPseudoHeaders
-
-=item ActOnPseudoHeaders $PseudoHeaders
-
-Takes a string of pseudo-headers, iterates through them and does what they tell it to.
-
-=cut
-
-sub ActOnPseudoHeaders {
- my $CurrentUser = shift;
- my (@actions) = (@_);
-
- my $ResultsMessage = '';
- my $Ticket = RT::Ticket->new($CurrentUser);
-
- foreach my $action (@actions) {
- my ($val);
- my $msg = '';
-
- $ResultsMessage .= ">>> $action\n";
-
- if ( $action =~ /^RT-(.*?):\s*(.*)$/i ) {
- my $command = $1;
- my $args = $2;
-
- if ( $command =~ /^ticket$/i ) {
-
- $val = $Ticket->Load($args);
- unless ($val) {
- $ResultsMessage .=
- loc("ERROR: Couldn't load ticket '[_1]': [_2].\n", $1, $msg);
- . loc("Aborting to avoid unintended ticket modifications.\n")
- . loc("The following commands were not proccessed:\n\n")
- . join ( "\n", @actions );
- return ($ResultsMessage);
- }
- $ResultsMessage .= loc("Ticket [_1] loaded\n", $Ticket->Id);
- }
- else {
- unless ( $Ticket->Id ) {
- $ResultsMessage .= loc("No Ticket specified. Aborting ticket ")
- . loc("modifications\n\n")
- . loc("The following commands were not proccessed:\n\n")
- . join ( "\n", @actions );
- return ($ResultsMessage);
- }
-
- # Deal with the basics
- if ( $command =~ /^(Subject|Owner|Status|Queue)$/i ) {
- my $method = 'Set' . ucfirst( lc($1) );
- ( $val, $msg ) = $Ticket->$method($args);
- }
-
- # Deal with the dates
- elsif ( $command =~ /^(due|starts|started|resolved)$/i ) {
- my $method = 'Set' . ucfirst( lc($1) );
- my $date = new RT::Date($CurrentUser);
- $date->Set( Format => 'unknown', Value => $args );
- ( $val, $msg ) = $Ticket->$method( $date->ISO );
- }
-
- # Deal with the watchers
- elsif ( $command =~ /^(requestor|requestors|cc|admincc)$/i ) {
- my $operator = "+";
- my ($type);
- if ( $args =~ /^(\+|\-)(.*)$/ ) {
- $operator = $1;
- $args = $2;
- }
- $type = 'Requestor' if ( $command =~ /^requestor/i );
- $type = 'Cc' if ( $command =~ /^cc/i );
- $type = 'AdminCc' if ( $command =~ /^admincc/i );
-
- my $user = RT::User->new($CurrentUser);
- $user->Load($args);
-
- if ($operator eq '+') {
- ($val, $msg) = $Ticket->AddWatcher( Type => $type,
- PrincipalId => $user->PrincipalId);
- } elsif ($operator eq '-') {
- ($val, $msg) = $Ticket->DeleteWatcher( Type => $type,
- PrincipalId => $user->PrincipalId);
- }
-
- }
- $ResultsMessage .= $msg . "\n";
- }
-
- }
- return ($ResultsMessage);
-
-}
-
-# }}}
-
-# {{{ sub untaint
-sub untaint {
- my $val = shift;
-
- if ( $val =~ /^([-\#\/\w.]+)$/ ) {
- $val = $1; # $data now untainted
- }
- else {
- die loc("Bad data in [_1]", $val); # log this somewhere
- }
- return ($val);
-}
-
-# }}}
-
-=head1 AUTHOR
-
-
-
- rt-commit-handler is a rewritten version of the NetBSD commit handler,
- which was placed in the public domain by Charles Hannum. It bore the following
- authors statement:
-
- Contributed by David Hampton <hampton@cisco.com>
- Hacked greatly by Greg A. Woods <woods@planix.com>
- Rewritten by Charles M. Hannum <mycroft@netbsd.org>
-
-=cut
-
diff --git a/rt/bin/rt-crontool b/rt/bin/rt-crontool
index 563a272fa..8fcd63111 100644
--- a/rt/bin/rt-crontool
+++ b/rt/bin/rt-crontool
@@ -2,8 +2,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -25,7 +25,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+# http://www.gnu.org/copyleft/gpl.html.
#
#
# CONTRIBUTION SUBMISSION POLICY:
@@ -49,32 +49,9 @@
use strict;
use Carp;
-# fix lib paths, some may be relative
-BEGIN {
- require File::Spec;
- my @libs = ("lib", "local/lib");
- my $bin_path;
-
- for my $lib (@libs) {
- unless ( File::Spec->file_name_is_absolute($lib) ) {
- unless ($bin_path) {
- if ( File::Spec->file_name_is_absolute(__FILE__) ) {
- $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
- }
- else {
- require FindBin;
- no warnings "once";
- $bin_path = $FindBin::Bin;
- }
- }
- $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
- }
- unshift @INC, $lib;
- }
+use lib ("/opt/rt3/local/lib", "/opt/rt3/lib");
-}
-
-use RT;
+package RT;
use Getopt::Long;
@@ -85,66 +62,54 @@ use RT::Template;
#Clean out all the nasties from the environment
CleanEnv();
-my ( $search, $condition, $action, $search_arg, $condition_arg, $action_arg,
- $template, $template_id, $transaction, $transaction_type, $help, $log, $verbose );
-GetOptions(
- "search=s" => \$search,
- "search-arg=s" => \$search_arg,
- "condition=s" => \$condition,
- "condition-arg=s" => \$condition_arg,
- "action-arg=s" => \$action_arg,
- "action=s" => \$action,
- "template=s" => \$template,
- "template-id=s" => \$template_id,
- "transaction=s" => \$transaction,
- "transaction-type=s" => \$transaction_type,
- "log=s" => \$log,
- "verbose|v" => \$verbose,
- "help" => \$help,
-);
-
# Load the config file
RT::LoadConfig();
-# adjust logging to the screen according to options
-RT->Config->Set( LogToScreen => $log ) if $log;
-
#Connect to the database and get RT::SystemUser and RT::Nobody loaded
RT::Init();
#Get the current user all loaded
my $CurrentUser = GetCurrentUser();
-# show help even if there is no current user
-help() if $help;
-
unless ( $CurrentUser->Id ) {
print loc("No RT user found. Please consult your RT administrator.\n");
exit(1);
}
-help() unless $search && $action;
-
-$transaction = lc( $transaction||'' );
-if ( $transaction && $transaction !~ /^(first|all|last)$/i ) {
- print STDERR loc("--transaction argument could be only 'first', 'last' or 'all'");
- exit 1;
-}
-
-if ( $template && $template_id ) {
- print STDERR loc("--template-id is deprecated argument and can not be used with --template");
+my ( $search, $condition, $action, $search_arg, $condition_arg, $action_arg,
+ $template_id, $transaction, $transaction_type, $help, $verbose );
+GetOptions( "search=s" => \$search,
+ "search-arg=s" => \$search_arg,
+ "condition=s" => \$condition,
+ "condition-arg=s" => \$condition_arg,
+ "action-arg=s" => \$action_arg,
+ "action=s" => \$action,
+ "template-id=s" => \$template_id,
+ "transaction=s" => \$transaction,
+ "transaction-type=s" => \$transaction_type,
+ "help" => \$help,
+ "verbose|v" => \$verbose );
+
+help() if $help or not $search or not $action;
+
+$transaction ||= 'first';
+unless ( $transaction =~ /^(first|last)$/i ) {
+ print STDERR loc("--transaction argument could be only 'first' or 'last'");
exit 1;
}
-elsif ( $template_id ) {
-# don't warn
- $template = $template_id;
-}
+$transaction = lc($transaction) eq 'first'? 'ASC': 'DESC';
# We _must_ have a search object
load_module($search);
load_module($action) if ($action);
load_module($condition) if ($condition);
+# load template if specified
+my $template_obj;
+if ($template_id) {
+ $template_obj = RT::Template->new($CurrentUser);
+ $template_obj->Load($template_id);
+}
my $void_scrip = RT::Scrip->new( $CurrentUser );
my $void_scrip_action = RT::ScripAction->new( $CurrentUser );
@@ -167,31 +132,9 @@ my $tickets = $search->TicketsObj;
while ( my $ticket = $tickets->Next() ) {
print $ticket->Id() . ": " if ($verbose);
- my $template_obj = get_template( $ticket );
-
- if ( $transaction ) {
- my $txns = get_transactions($ticket);
- my $found = 0;
- while ( my $txn = $txns->Next ) {
- print loc("Using transaction #[_1]...", $txn->id)
- if $verbose;
- process($ticket, $txn, $template_obj);
- $found = 1;
- }
- print loc("Couldn't find suitable transaction, skipping")
- if $verbose && !$found;
- } else {
- print loc("Processing without transaction, some conditions and actions may fail. Consider using --transaction argument")
- if $verbose;
-
- process($ticket, undef, $template_obj);
- }
-}
-
-sub process {
- my $ticket = shift;
- my $transaction = shift;
- my $template_obj = shift;
+ my $transaction = get_transaction($ticket);
+ print loc("Using transaction #[_1]...", $transaction->id)
+ if $verbose && $transaction;
# perform some more advanced check
if ($condition) {
@@ -206,8 +149,8 @@ sub process {
# if the condition doesn't apply, get out of here
- return unless $condition_obj->IsApplicable;
- print loc("Condition matches...") if $verbose;
+ next unless ( $condition_obj->IsApplicable );
+ print loc("Condition matches...") if ($verbose);
}
#prepare our action
@@ -222,76 +165,34 @@ sub process {
);
#if our preparation, move onto the next ticket
- return unless $action_obj->Prepare;
- print loc("Action prepared...") if $verbose;
+ next unless ( $action_obj->Prepare );
+ print loc("Action prepared...") if ($verbose);
#commit our action.
- return unless $action_obj->Commit;
- print loc("Action committed.\n") if $verbose;
+ next unless ( $action_obj->Commit );
+ print loc("Action committed.\n") if ($verbose);
}
-=head2 get_transactions
+=head2 get_transaction
-Takes ticket and returns L<RT::Transactions> object with transactions
-of the ticket according to command line arguments C<--transaction>
-and <--transaction-type>.
+Takes ticket and returns its transaction acording to command
+line arguments C<--transaction> and <--transaction-type>.
=cut
-sub get_transactions {
+sub get_transaction {
my $ticket = shift;
my $txns = $ticket->Transactions;
- my $order = $transaction eq 'last'? 'DESC': 'ASC';
$txns->OrderByCols(
- { FIELD => 'Created', ORDER => $order },
- { FIELD => 'id', ORDER => $order },
+ { FIELD => 'Created', ORDER => $transaction },
+ { FIELD => 'id', ORDER => $transaction },
);
- if ( $transaction_type ) {
- $transaction_type =~ s/^\s+//;
- $transaction_type =~ s/\s+$//;
- foreach my $type ( split /\s*,\s*/, $transaction_type ) {
- $txns->Limit( FIELD => 'Type', VALUE => $type, ENTRYAGGREGATOR => 'OR' );
- }
- }
- $txns->RowsPerPage(1) unless $transaction eq 'all';
- return $txns;
+ $txns->Limit( FIELD => 'Type', VALUE => $transaction_type )
+ if $transaction_type;
+ $txns->RowsPerPage(1);
+ return $txns->First;
}
-=head2 get_template
-
-Takes a ticket and returns a template according to command line options.
-
-=cut
-
-{ my $cache = undef;
-sub get_template {
- my $ticket = shift;
- return undef unless $template;
-
- unless ( $template =~ /\D/ ) {
- # by id
- return $cache if $cache;
-
- my $cache = RT::Template->new( $RT::SystemUser );
- $cache->Load( $template );
- die "Failed to load template '$template'"
- unless $cache->id;
- return $cache;
- }
-
- my $queue = $ticket->Queue;
- return $cache->{ $queue } if $cache->{ $queue };
-
- my $res = RT::Template->new( $RT::SystemUser );
- $res->LoadQueueTemplate( Queue => $queue, Name => $template );
- unless ( $res->id ) {
- $res->LoadGlobalTemplate( $template );
- die "Failed to load template '$template', either for queue #$queue or global"
- unless $res->id;
- }
- return $cache->{ $queue } = $res;
-} }
-
# {{{ load_module
=head2 load_module
@@ -335,33 +236,31 @@ sub help {
. loc( "[_1] - Specify the search module you want to use", "--search" )
. "\n";
print " "
- . loc( "[_1] - An argument to pass to [_2]", "--search-arg", "--search" )
+ . loc( "[_1] - An argument to pass to [_2]", "--search-argument", "--search" )
. "\n";
print " "
. loc( "[_1] - Specify the condition module you want to use", "--condition" )
. "\n";
print " "
- . loc( "[_1] - An argument to pass to [_2]", "--condition-arg", "--condition" )
+ . loc( "[_1] - An argument to pass to [_2]", "--condition-argument", "--condition" )
. "\n";
print " "
. loc( "[_1] - Specify the action module you want to use", "--action" )
. "\n";
print " "
- . loc( "[_1] - An argument to pass to [_2]", "--action-arg", "--action" )
+ . loc( "[_1] - An argument to pass to [_2]", "--action-argument", "--action" )
. "\n";
print " "
- . loc( "[_1] - Specify name or id of template(s) you want to use", "--template" )
+ . loc( "[_1] - Specify id of the template you want to use", "--template-id" )
. "\n";
print " "
- . loc( "[_1] - Specify if you want to use either 'first', 'last' or 'all' transactions", "--transaction" )
+ . loc( "[_1] - Specify if you want to use either 'first' or 'last' transaction", "--transaction" )
. "\n";
print " "
- . loc( "[_1] - Specify the comma separated list of transactions' types you want to use", "--transaction-type" )
+ . loc( "[_1] - Specify the type of a transaction you want to use", "--transaction-type" )
. "\n";
print " "
- . loc( "[_1] - Adjust LogToScreen config option", "--log" ) . "\n";
- print " "
. loc( "[_1] - Output status updates to STDOUT", "--verbose" ) . "\n";
print "\n";
print "\n";
diff --git a/rt/bin/rt-mailgate b/rt/bin/rt-mailgate
index abe731196..8db26dbe3 100755
--- a/rt/bin/rt-mailgate
+++ b/rt/bin/rt-mailgate
@@ -2,8 +2,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2007 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -25,7 +25,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+# http://www.gnu.org/copyleft/gpl.html.
#
#
# CONTRIBUTION SUBMISSION POLICY:
@@ -52,34 +52,30 @@ rt-mailgate - Mail interface to RT3.
=cut
+
use strict;
use warnings;
-
use Getopt::Long;
use LWP::UserAgent;
-use HTTP::Request::Common qw($DYNAMIC_FILE_UPLOAD);
-$DYNAMIC_FILE_UPLOAD = 1;
use constant EX_TEMPFAIL => 75;
-use constant BUFFER_SIZE => 8192;
my %opts;
GetOptions( \%opts, "queue=s", "action=s", "url=s", "jar=s", "help", "debug", "extension=s", "timeout=i" );
-if ( $opts{'help'} ) {
+if ( $opts{help} ) {
require Pod::Usage;
import Pod::Usage;
pod2usage("RT Mail Gateway\n");
exit 1; # Don't want to succeed if this is really an email!
}
-unless ( $opts{'url'} ) {
- print STDERR "$0 invoked improperly\n\nNo 'url' provided to mail gateway!\n";
- exit 1;
+for (qw(url)) {
+ die "$0 invoked improperly\n\nNo $_ provided to mail gateway!\n" unless $opts{$_};
}
-my $ua = new LWP::UserAgent;
-$ua->cookie_jar( { file => $opts{'jar'} } ) if $opts{'jar'};
+my $ua = LWP::UserAgent->new();
+$ua->cookie_jar( { file => $opts{jar} } );
my %args = (
SessionType => 'REST', # Surpress login box
@@ -88,54 +84,37 @@ foreach ( qw(queue action) ) {
$args{$_} = $opts{$_} if defined $opts{$_};
};
-if ( ($opts{'extension'} || '') =~ /^(?:action|queue|ticket)$/i ) {
- $args{ lc $opts{'extension'} } = $ENV{'EXTENSION'} || $opts{$opts{'extension'}};
-} elsif ( $opts{'extension'} && $ENV{'EXTENSION'} ) {
- print STDERR "Value of the --extension argument is not action, queue or ticket"
- .", but environment variable EXTENSION is also defined. The former is ignored.\n";
-}
+# Read the message in from STDIN
+$args{'message'} = do { local (@ARGV, $/); <> };
-# add ENV{'EXTENSION'} as X-RT-MailExtension to the message header
-if ( my $value = ( $ENV{'EXTENSION'} || $opts{'extension'} ) ) {
- # prepare value to avoid MIME format breakage
- # strip trailing newline symbols
- $value =~ s/(\r*\n)+$//;
- # make a correct multiline header field,
- # with tabs in the beginning of each line
- $value =~ s/(\r*\n)/$1\t/g;
- $opts{'headers'} .= "X-RT-Mail-Extension: $value\n";
+unless ( $args{message} =~ /\S/ ) {
+ print STDERR "$0: no message passed on STDIN!\n";
+ exit 0;
}
-# Read the message in from STDIN
-my %message = write_down_message();
-unless( $message{'filename'} ) {
- $args{'message'} = [
- undef, '',
- 'Content-Type' => 'application/octet-stream',
- Content => ${ $message{'content'} },
- ];
-} else {
- $args{'message'} = [
- $message{'filename'}, '',
- 'Content-Type' => 'application/octet-stream',
- ];
+if ($opts{'extension'}) {
+ $args{$opts{'extension'}} = $ENV{'EXTENSION'};
}
+# Set up cookie here.
+
my $full_url = $opts{'url'}. "/REST/1.0/NoAuth/mail-gateway";
-print STDERR "$0: connecting to $full_url\n" if $opts{'debug'};
+warn "Connecting to $full_url" if $opts{'debug'};
-$ua->timeout( exists( $opts{'timeout'} )? $opts{'timeout'}: 180 );
-my $r = $ua->post( $full_url, \%args, Content_Type => 'form-data' );
+
+
+$ua->timeout(exists($opts{'timeout'}) ? $opts{'timeout'} : 180);
+my $r = $ua->post( $full_url, {%args} );
check_failure($r);
my $content = $r->content;
-print STDERR $content ."\n" if $opts{'debug'};
+warn $content if ($opts{debug});
if ( $content !~ /^(ok|not ok)/ ) {
# It's not the server's fault if the mail is bogus. We just want to know that
# *something* came out of the server.
- print STDERR <<EOF;
+ warn <<EOF;
RT server error.
The RT server which handled your email did not behave as expected. It
@@ -144,19 +123,16 @@ said:
$content
EOF
- exit EX_TEMPFAIL;
+exit EX_TEMPFAIL;
+
}
exit;
-END {
- unlink $message{'filename'} if $message{'filename'};
-}
-
sub check_failure {
my $r = shift;
- return if $r->is_success;
+ return if $r->is_success();
# This ordinarily oughtn't to be able to happen, suggests a bug in RT.
# So only load these heavy modules when they're needed.
@@ -164,64 +140,17 @@ sub check_failure {
require HTML::FormatText;
my $error = $r->error_as_HTML;
- my $tree = HTML::TreeBuilder->new->parse( $error );
+ my $tree = HTML::TreeBuilder->new->parse($error);
$tree->eof;
# It'll be a cold day in hell before RT sends out bounces in HTML
- my $formatter = HTML::FormatText->new(
- leftmargin => 0,
- rightmargin => 50,
- );
- print STDERR $formatter->format( $tree );
- print STDERR "\n$0: undefined server error\n" if $opts{'debug'};
+ my $formatter = HTML::FormatText->new( leftmargin => 0,
+ rightmargin => 50 );
+ warn $formatter->format($tree);
+ warn "This is $0 exiting because of an undefined server error" if ($opts{debug});
exit EX_TEMPFAIL;
}
-sub write_down_message {
- use File::Temp qw(tempfile);
-
- local $@;
- my ($fh, $filename) = eval { tempfile() };
- if ( !$fh || $@ ) {
- print STDERR "$0: Couldn't create temp file, using memory\n";
- print STDERR "error: $@\n" if $@;
-
- my $message = \do { local (@ARGV, $/); <> };
- unless ( $$message =~ /\S/ ) {
- print STDERR "$0: no message passed on STDIN\n";
- exit 0;
- }
- $$message = $opts{'headers'} . $$message if $opts{'headers'};
- return ( content => $message );
- }
-
- binmode $fh;
- binmode \*STDIN;
-
- print $fh $opts{'headers'} if $opts{'headers'};
-
- my $buf; my $empty = 1;
- while(1) {
- my $status = read \*STDIN, $buf, BUFFER_SIZE;
- unless ( defined $status ) {
- print STDERR "$0: couldn't read message: $!\n";
- exit EX_TEMPFAIL;
- } elsif ( !$status ) {
- last;
- }
- $empty = 0 if $buf =~ /\S/;
- print $fh $buf;
- };
- close $fh;
-
- if ( $empty ) {
- print STDERR "$0: no message passed on STDIN\n";
- exit 0;
- }
- print STDERR "$0: temp file is '$filename'\n" if $opts{'debug'};
- return (filename => $filename);
-}
-
=head1 SYNOPSIS
@@ -237,6 +166,8 @@ Usual invocation (from MTA):
+See C<man rt-mailgate> for more.
+
=head1 OPTIONS
=over 3
@@ -247,7 +178,7 @@ Specifies what happens to email sent to this alias. The avaliable
basic actions are: C<correspond>, C<comment>.
-If you've set the RT configuration variable B<< C<UnsafeEmailCommands> >>,
+If you've set the RT configuration variable B<$RT::UnsafeEmailCommands>,
C<take> and C<resolve> are also available. You can execute two or more
actions on a single message using a C<-> separated list. RT will execute
the actions in the listed order. For example you can use C<take-comment>,
@@ -328,13 +259,13 @@ there are situations in which you will want to authenticate users
before allowing them to communicate with the system. You can do this
via a plug-in mechanism in the RT configuration.
-You can set the array C<@MailPlugins> to be a list of plugins. The
+You can set the array C<@RT::MailPlugins> to be a list of plugins. The
default plugin, if this is not given, is C<Auth::MailFrom> - that is,
authentication of the person is done based on the C<From> header of the
email. If you have additional filters or authentication mechanisms, you
can list them here and they will be called in order:
- Set( @MailPlugins =>
+ @RT::MailPlugins = (
"Filter::SpamAssassin",
"Auth::LDAP",
# ...
@@ -342,12 +273,12 @@ can list them here and they will be called in order:
See the documentation for any additional plugins you have.
-You may also put Perl subroutines into the C<@MailPlugins> array, if
+You may also put Perl subroutines into the C<@RT::MailPlugins> array, if
they behave as described below.
=head1 WRITING PLUGINS
-What's actually going on in the above is that C<@MailPlugins> is a
+What's actually going on in the above is that C<@RT::MailPlugins> is a
list of Perl modules; RT prepends C<RT::Interface::Email::> to the name,
to form a package name, and then C<use>'s this module. The module is
expected to provide a C<GetCurrentUser> subroutine, which takes a hash of
@@ -388,22 +319,5 @@ the correspondent) or one, which is the normal mode of operation.
Additionally, if C<-1> is returned, then the processing of the plug-ins
stops immediately and the message is ignored.
-=head1 ENVIRONMENT
-
-=over 4
-
-=item EXTENSION
-
-Some MTAs will route mail sent to user-foo@host or user+foo@host to user@host
-and present "foo" in the environment variable C<EXTENSION>. Mailgate adds value
-of this variable to message in the C<X-RT-Mail-Extension> field of the message
-header.
-
-See also C<--extension> option. Note that value of the environment variable is
-always added to the message header when it's not empty even if C<--extension>
-option is not provided.
-
-=back 4
-
=cut
diff --git a/rt/bin/webmux.pl b/rt/bin/webmux.pl
deleted file mode 100755
index cb428ad2c..000000000
--- a/rt/bin/webmux.pl
+++ /dev/null
@@ -1,178 +0,0 @@
-#!/usr/bin/perl
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-use strict;
-
-BEGIN {
- $ENV{'PATH'} = '/bin:/usr/bin'; # or whatever you need
- $ENV{'CDPATH'} = '' if defined $ENV{'CDPATH'};
- $ENV{'SHELL'} = '/bin/sh' if defined $ENV{'SHELL'};
- $ENV{'ENV'} = '' if defined $ENV{'ENV'};
- $ENV{'IFS'} = '' if defined $ENV{'IFS'};
-
- use CGI qw(-private_tempfiles); #bring this in before mason, to make sure we
- #set private_tempfiles
-
- die "RT does not support mod_perl 1.99. Please upgrade to mod_perl 2.0"
- if $ENV{'MOD_PERL'}
- and $ENV{'MOD_PERL'} =~ m{mod_perl/(?:1\.9)};
-
-}
-
-# fix lib paths, some may be relative
-BEGIN {
- require File::Spec;
- my @libs = ("lib", "local/lib");
- my $bin_path;
-
- for my $lib (@libs) {
- unless ( File::Spec->file_name_is_absolute($lib) ) {
- unless ($bin_path) {
- if ( File::Spec->file_name_is_absolute(__FILE__) ) {
- $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
- }
- else {
- require FindBin;
- no warnings "once";
- $bin_path = $FindBin::Bin;
- }
- }
- $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
- }
- unshift @INC, $lib;
- }
-
-}
-use RT;
-
-package RT::Mason;
-
-use vars qw($Nobody $SystemUser $Handler $r);
-
-#This drags in RT's config.pm
-BEGIN {
- RT::LoadConfig();
- if (RT->Config->Get('DevelMode')) { require Module::Refresh; }
- RT->InitPluginPaths();
-}
-
-{
- require RT::Handle;
- my $dsn = RT::Handle->DSN;
- my $user = RT->Config->Get('DatabaseUser');
- my $pass = RT->Config->Get('DatabasePassword');
-
- my $dbh = DBI->connect(
- $dsn, $user, $pass,
- { RaiseError => 0, PrintError => 0 },
- );
- if ( $dbh ) {
- my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'post' );
- die $msg unless $status;
- }
-}
-
-{
- package HTML::Mason::Commands;
- use vars qw(%session);
-}
-
-use RT::Interface::Web;
-use RT::Interface::Web::Handler;
-
-if ($ENV{'MOD_PERL'} && !RT->Config->Get('DevelMode')) {
- # Under static_source, we need to purge the component cache
- # each time we restart, so newer components may be reloaded.
- #
- # We can't do this in FastCGI or we'll blow away the component root _every_ time a new server starts
- # which happens every few hits.
-
- use File::Path qw( rmtree );
- use File::Glob qw( bsd_glob );
- my @files = bsd_glob("$RT::MasonDataDir/obj/*");
- rmtree([ @files ], 0, 1) if @files;
-}
-
-sub handler {
- ($r) = @_;
-
- local $SIG{__WARN__};
- local $SIG{__DIE__};
-
- if ($r->content_type =~ m/^httpd\b.*\bdirectory/i) {
- use File::Spec::Unix;
- # Our DirectoryIndex is always index.html, regardless of httpd settings
- $r->filename( File::Spec::Unix->catfile( $r->filename, 'index.html' ) );
- }
-# elsif (defined( $r->content_type )) {
- #$r->content_type !~ m!(^text/|\bxml\b)!i or return -1;
-# }
-
- Module::Refresh->refresh if RT->Config->Get('DevelMode');
-
- RT::Init();
-
- $Handler ||= RT::Interface::Web::Handler->new(
- RT->Config->Get('MasonParameters')
- );
-
- my %session;
- my $status;
- eval { $status = $Handler->handle_request($r) };
- if ($@) {
- $RT::Logger->crit($@);
- }
-
- undef(%session);
-
- RT::Interface::Web::Handler->CleanupRequest();
-
- return $status;
-}
-
-1;
diff --git a/rt/config b/rt/config
deleted file mode 100644
index b9418a66d..000000000
--- a/rt/config
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * This is the project ``config'' file. It controls many aspects of
- * how Aegis interacts with your project.
- *
- * There are several sections of this file, each dealing with a different
- * aspect of the interaction between Aegis and the tools used to manage
- * yout project.
- */
-
-/*
- * -------------------------------------------------------------------------
- *
- * The build tool is delegated.
- */
-
-/*
- * The build_command field of the config file is used to invoke the relevant
- * build command. The following command tells cook where to find the recipes.
- * The ${s Howto.cook} expands to a path into the baseline during development
- * if the file is not in the change. Look in aesub(5) for more information
- * about command substitutions.
- */
-build_command =
- "";
-
-/* cook -book ${s Howto.cook} search_path=$search_path \
-project=$p change=$c version=$v -star -no-log -action -notouch";
-
-/*
- * The recipes in the User Guide will all remove their targets before
- * constructing them, which qualifies them to use the following entry in the
- * config file. The targets MUST be removed first if this field is true,
- * otherwise the baseline would cease to be self-consistent.
- *
- * Fortunately, Cook has a nifty ``set unlink;'' statement which is
- * placed at the top of the cookbook.
- */
-link_integration_directory = true;
-
-
-/*
- * -------------------------------------------------------------------------
- *
- * The history tool is delegated.
- *
- * The fhist program was written by David I. Bell and is admirably
- * suited to providing a history mechanism with out the "cruft" that
- * SCCS and RCS impose. The fhist program also comes with two other
- * utilities, fcomp and fmerge, which use the same minimal difference
- * algorithm.
- *
- * Please note that the [# edit #] feature needs to be avoided, or the
- * -Fored_Update (-fu) flag needs to be used in addition to the
- * -Conditional_Update (-cu) flag, otherwise updates will complain that
- * ``Input file "XXX" contains edit A instead of B for module "YYY"''
- *
- * The history_create_command and the history_put_command are
- * intentionally identical. This minimizes problems when using
- * branches.
- *
- * The ${quote ...} construct is used to quote filesnames whicg contain
- * shell special characters. A minimum of quoting is performed, so if
- * the filenames do not contail shell special characters, no quotes will
- * be used.
- */
-
-/*
- * This command is used to create a new project history. The command is
- * always executed as the project owner. Note he the source is left in
- * the baseline. The following substitutions are available:
- *
- * ${Input}
- * absolute path of the source file
- * ${History}
- * absolute path of the history file
- *
- * The history_create_command and the history_put_command are
- * intentionally identical. This minimizes problems when using
- * branches.
- */
-history_create_command =
- "fhist ${quote ${basename $input}} -cr -cu -i ${quote $input} \
--p ${quote ${dirname $history}} -r";
-
-/*
- * This command is used to get a specific edit back from history. The
- * command may be executed by developers. The following substitutions
- * are available:
- *
- * ${History}
- * absolute path of the history file
- * ${Edit}
- * edit number, as given by history_query_command
- * ${Output}
- * absolute path of the destination file
- *
- * Note that the destination filename will never look anything like the
- * history source filename, so the -p is essential.
- */
-history_get_command =
- "fhist ${quote ${basename $history}} -e ${quote $e} \
--o ${quote $output} -p ${quote ${dirname $history}}";
-
-/*
- * This command is used to add a new "top-most" entry to the history
- * file. This command is always executed as the project owner. Note
- * that the source file is left in the baseline. The following
- * substitutions are available:
- *
- * ${Input}
- * absolute path of source file
- * ${History}
- * absolute path of history file
- *
- * The history_create_command and the history_put_command are
- * intentionally identical. This minimizes problems when using
- * branches.
- */
-history_put_command =
- "fhist ${quote ${basename $input}} -cr -cu -i ${quote $input} \
--p ${quote ${dirname $history}} -r";
-
-/*
- * This command is used to query what the history mechanism calls the
- * "top-most" edit of a history file. The result may be any arbitrary
- * string, it need not be anything like a number, just so long as it
- * uniquely identifies the edit for use by the history_get_command at a
- * later date. The edit number is to be printed on the standard output.
- * This command may be executed by developers. The following
- * substitutions are available:
- *
- * ${History}
- * absolute path of the history file
- */
-history_query_command =
- "fhist ${quote ${basename $history}} -l 0 \
--p ${quote ${dirname $history}} -q";
-
-/*
- * -------------------------------------------------------------------------
- *
- * The difference and merge tools are delegated.
- */
-
-/*
- * Compare two files using fcomp. The -w option produces an output of
- * the entire file, with insertions an deletions marked by "change bars"
- * in the left margin. This is superior to context difference, as it
- * shows the entire file as context. The -s option could be added to
- * compare runs of white space as equal.
- *
- * This command is used by aed(1) to produce a difference listing when
- * file in the development directory was originally copied from the
- * current version in the baseline.
- *
- * All of the command substitutions described in aesub(5) are available.
- * In addition, the following substitutions are also available:
- *
- * ${ORiginal}
- * The absolute path name of a file containing the version
- * originally copied. Usually in the baseline.
- * ${Input}
- * The absolute path name of the edited version of the file.
- * Usually in the development directory.
- * ${Output}
- * The absolute path name of the file in which to write the
- * difference listing. Usually in the development directory.
- *
- * An exit status of 0 means successful, even of the files differ (and
- * they usually do). An exit status which is non-zero means something
- * is wrong.
- *
- * The non-zero exit status may be used to overload this command with
- * extra tests, such as line length limits. The difference files must
- * be produced in addition to these extra tests.
- */
-diff_command =
- "fcomp -w ${quote $original} ${quote $input} -o ${quote $output}";
-
-/*
- * Compare three files using fmerge. Conflicts are marked in the
- * output.
- *
- * This command is used by aed(1) to produce a difference listing when a
- * file in the development directory is out of date compared to the
- * current version in the baseline.
- *
- * All of the command substitutions described in aesub(5) are available.
- * In addition, the following substitutions are also available:
- *
- * ${ORiginal}
- * The absolute path name of a file containing the common ancestor
- * version of ${MostRecent} and {$Input}. Usually the version
- * originally copied into the change. Usually in a temporary file.
- * ${Most_Recent}
- * The absolute path name of a file containing the most recent
- * version. Usually in the baseline.
- * ${Input}
- * The absolute path name of the edited version of the file.
- * Usually in the development directory.
- * ${Output}
- * The absolute path name of the file in which to write the
- * difference listing. Usually in the development directory.
- *
- * An exit status of 0 means successful, even of the files differ (and
- * they usually do). An exit status which is non-zero means something
- * is wrong.
- */
-merge_command =
- "fmerge ${quote $original} ${quote $MostRecent} ${quote $input} \
--o ${quote $output} -c /dev/null";
-
-/*
- * -------------------------------------------------------------------------
- *
- * The new file templates are very handy. They allow all sorts of things
- * to be se automatically. You need to edit them to add your own name,
- * and copyright conditions.
- */
-
-file_template =
-[
- {
- pattern = [ "*" ];
- body = "${read_file ${source etc/template/generic abs}}";
-
- }
-];
-
-/* -------------------------------------------------------------------------
- *
- * The integrate_begin_exceptions are files which are not hard linked
- * from the baseline to the integration directory. In this case, this
- * is done to ensure the version stmp is updated appropriately.
- */
-
-integrate_begin_exceptions = [ ];
-
-
-
-
-/* -------------------------------------------------------------------------
- *
- * The trojan_horse_suspect field is a list of filename patterns which
- * indicate files which *could* host a Trojan horse attack. It makes
- * aedist --receive more cautions. It is NOT a silver bullet: just
- * about ANY file can host a Trojan, one way or the other.
- */
-
-trojan_horse_suspect = [ ];
-
-build_covers_all_architectures = true;
-
-test_command = "make test";
-
-build_time_adjust=dont_adjust;
diff --git a/rt/config.layout b/rt/config.layout.in
index 52fcef1ee..a08f48948 100644
--- a/rt/config.layout
+++ b/rt/config.layout.in
@@ -23,7 +23,6 @@
sbindir: ${exec_prefix}/sbin
sysconfdir: ${prefix}/etc
mandir: ${prefix}/man
- plugindir: ${prefix}/plugins
libdir: ${prefix}/lib
datadir: ${prefix}/share
htmldir: ${datadir}/html
@@ -39,16 +38,15 @@
customlibdir: ${customdir}/lib
</Layout>
<Layout inplace>
- prefix: .
+ prefix: `pwd`
exec_prefix: ${prefix}
bindir: ${exec_prefix}/bin
sbindir: ${exec_prefix}/sbin
sysconfdir: ${prefix}/etc
mandir: ${prefix}/man
- plugindir: ${prefix}/plugins
libdir: ${prefix}/lib
datadir: ${prefix}/share
- htmldir: ${datadir}/html
+ htmldir: ${prefix}/html
manualdir: ${datadir}/doc
localstatedir: ${prefix}/var
logfiledir: ${localstatedir}/log
@@ -61,32 +59,6 @@
customlibdir: ${customdir}/lib
</Layout>
-<Layout FHS>
- prefix: /usr/local
- exec_prefix: ${prefix}
- bindir: ${prefix}/bin
- sbindir: ${prefix}/sbin
- sysconfdir: /etc+
- datadir: ${prefix}/share
-# FIXME: missing support for lib64
- libdir: ${prefix}/lib
- mandir: ${datadir}/man
-# FIXME: no such directory in FHS; shouldn't go to somewhere in "${datadir}/rt/"?
- plugindir: ${datadir}/plugins
- htmldir: ${datadir}/html
- manualdir: ${datadir}/doc
- localstatedir: /var
- logfiledir: ${localstatedir}/log
-# XXX: "/var/cache/mason/*"?
- masonstatedir: ${localstatedir}/cache/mason_data
- sessionstatedir: ${localstatedir}/cache/session_data
- customdir: ${prefix}/local
- custometcdir: ${customdir}/etc
- customhtmldir: ${customdir}/html
- customlexdir: ${customdir}/po
- customlibdir: ${customdir}/lib
-</Layout>
-
<Layout FreeBSD>
prefix: /usr/local
exec_prefix: ${prefix}
@@ -94,7 +66,6 @@
sbindir: ${exec_prefix}/sbin
sysconfdir: ${prefix}/etc+
mandir: ${prefix}/man
- plugindir: ${prefix}/plugins
libdir: ${prefix}/lib+
datadir: ${prefix}/share+
htmldir: ${datadir}/html
@@ -117,7 +88,6 @@
sbindir: ${exec_prefix}/sbin
sysconfdir: ${prefix}/etc
mandir: ${prefix}/man
- plugindir: ${prefix}/plugins
libdir: ${prefix}/lib
datadir: ${prefix}
htmldir: ${datadir}/html
@@ -133,49 +103,25 @@
customlibdir: ${customdir}/lib
</Layout>
-# RH path layout.
-<Layout RH>
- prefix: /usr
+<Layout Freeside>
+ prefix: /opt/rt3
exec_prefix: ${prefix}
bindir: ${exec_prefix}/bin
sbindir: ${exec_prefix}/sbin
- sysconfdir: /etc/rt
+ sysconfdir: ${prefix}/etc
mandir: ${prefix}/man
- libdir: ${prefix}/lib/rt
- datadir: /var/rt
- htmldir: ${datadir}/html
+ libdir: ${prefix}/lib
+ datadir: ${prefix}/share
+ htmldir: %%%FREESIDE_DOCUMENT_ROOT%%%/rt
manualdir: ${datadir}/doc
- plugindir: ${datadir}/plugins
- localstatedir: /var
- logfiledir: ${localstatedir}/log/rt
- masonstatedir: ${localstatedir}/rt/mason_data
- sessionstatedir: ${localstatedir}/rt/session_data
- customdir: ${prefix}/local/rt
- custometcdir: ${customdir}/etc
- customhtmldir: ${customdir}/html
- customlexdir: ${customdir}/po
- customlibdir: ${customdir}/lib
-</Layout>
-
-<Layout relative>
- prefix: /opt/rt3
- exec_prefix: ${prefix}
- bindir: bin
- sbindir: sbin
- sysconfdir: etc
- mandir: man
- plugindir: plugins
- libdir: lib
- datadir: share
- htmldir: ${datadir}/html
- manualdir: ${datadir}/doc
- localstatedir: var
+ localstatedir: ${prefix}/var
logfiledir: ${localstatedir}/log
- masonstatedir: ${localstatedir}/mason_data
+ masonstatedir: %%%MASONDATA%%%
sessionstatedir: ${localstatedir}/session_data
- customdir: local
+ customdir: ${prefix}/local
custometcdir: ${customdir}/etc
customhtmldir: ${customdir}/html
customlexdir: ${customdir}/po
customlibdir: ${customdir}/lib
</Layout>
+
diff --git a/rt/config.log b/rt/config.log
index 24e15e3cf..ab4b65c54 100644
--- a/rt/config.log
+++ b/rt/config.log
@@ -1,25 +1,25 @@
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by RT configure 3.0.9, which was
-generated by GNU Autoconf 2.53. Invocation command line was
+It was created by RT configure 3.6.4, which was
+generated by GNU Autoconf 2.59. Invocation command line was
- $ ./configure
+ $ ./configure --enable-layout=Freeside --with-db-type=Pg --with-db-dba=freeside --with-db-database=freeside --with-db-rt-user=freeside --with-db-rt-pass= --with-web-user=freeside --with-web-group=freeside --with-rt-group=freeside
## --------- ##
## Platform. ##
## --------- ##
-hostname = pallas
-uname -m = i686
-uname -r = 2.4.18-686
+hostname = rootwood
+uname -m = x86_64
+uname -r = 2.6.21-1-amd64
uname -s = Linux
-uname -v = #1 Sun Apr 14 11:32:47 EST 2002
+uname -v = #1 SMP Sat May 26 17:22:54 CEST 2007
/usr/bin/uname -p = unknown
/bin/uname -X = unknown
-/bin/arch = i686
+/bin/arch = unknown
/usr/bin/arch -k = unknown
/usr/convex/getsysinfo = unknown
hostinfo = unknown
@@ -27,41 +27,36 @@ hostinfo = unknown
/usr/bin/oslevel = unknown
/bin/universe = unknown
-PATH: /usr/X11R6/bin/
-PATH: /opt/rt/bin
-PATH: /usr/athena/bin
+PATH: /usr/local/sbin
PATH: /usr/local/bin
-PATH: /bin
-PATH: /usr/bin
PATH: /usr/sbin
PATH: /usr/bin
-PATH: /usr/games
-PATH: $HOME/bin
-PATH: /opt/kerberos/bin
-PATH: /opt/StarOffice-4.0/bin
-PATH: /opt/mysql/bin/
-PATH: .
+PATH: /sbin
+PATH: /bin
## ----------- ##
## Core tests. ##
## ----------- ##
-configure:1218: checking for a BSD-compatible install
-configure:1272: result: /usr/bin/install -c
-configure:1286: checking for perl
-configure:1304: found /usr/bin/perl
-configure:1317: result: /usr/bin/perl
-configure:1639: checking for chosen layout
-configure:1654: result: RT3
-configure:1986: creating ./config.status
+configure:1331: checking for a BSD-compatible install
+configure:1386: result: /usr/bin/install -c
+configure:1401: checking for gawk
+configure:1417: found /usr/bin/gawk
+configure:1427: result: gawk
+configure:1440: checking for perl
+configure:1458: found /usr/bin/perl
+configure:1471: result: /usr/bin/perl
+configure:1795: checking for chosen layout
+configure:1810: result: Freeside
+configure:2272: creating ./config.status
## ---------------------- ##
## Running config.status. ##
## ---------------------- ##
-This file was extended by RT config.status 3.0.9, which was
-generated by GNU Autoconf 2.53. Invocation command line was
+This file was extended by RT config.status 3.6.4, which was
+generated by GNU Autoconf 2.59. Invocation command line was
CONFIG_FILES =
CONFIG_HEADERS =
@@ -69,26 +64,22 @@ generated by GNU Autoconf 2.53. Invocation command line was
CONFIG_COMMANDS =
$ ./config.status
-on pallas
-
-config.status:639: creating sbin/rt-setup-database
-config.status:639: creating sbin/rt-test-dependencies
-config.status:639: creating Makefile
-config.status:639: creating etc/RT_Config.pm
-config.status:639: creating lib/RT.pm
-config.status:639: creating lib/t/00smoke.t
-config.status:639: creating lib/t/01harness.t
-config.status:639: creating lib/t/02regression.t
-config.status:639: creating lib/t/03web.pl
-config.status:639: creating lib/t/04_send_email.pl
-config.status:639: creating bin/mason_handler.fcgi
-config.status:639: creating bin/mason_handler.scgi
-config.status:639: creating bin/mason_handler.svc
-config.status:639: creating bin/rt-commit-handler
-config.status:639: creating bin/rt-crontool
-config.status:639: creating bin/rt-mailgate
-config.status:639: creating bin/rt
-config.status:639: creating bin/webmux.pl
+on rootwood
+
+config.status:760: creating sbin/rt-dump-database
+config.status:760: creating sbin/rt-setup-database
+config.status:760: creating sbin/rt-test-dependencies
+config.status:760: creating bin/mason_handler.fcgi
+config.status:760: creating bin/mason_handler.scgi
+config.status:760: creating bin/standalone_httpd
+config.status:760: creating bin/rt-crontool
+config.status:760: creating bin/rt-mailgate
+config.status:760: creating bin/rt
+config.status:760: creating Makefile
+config.status:760: creating etc/RT_Config.pm
+config.status:760: creating lib/RT.pm
+config.status:760: creating bin/mason_handler.svc
+config.status:760: creating bin/webmux.pl
## ---------------- ##
## Cache variables. ##
@@ -104,15 +95,132 @@ ac_cv_env_target_alias_set=
ac_cv_env_target_alias_value=
ac_cv_path_PERL=/usr/bin/perl
ac_cv_path_install='/usr/bin/install -c'
+ac_cv_prog_AWK=gawk
+
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+
+APACHECTL='/usr/sbin/apachectl'
+AWK='gawk'
+BIN_OWNER='root'
+CONFIG_FILE_PATH='/opt/rt3/etc'
+DATABASE_ENV_PREF=''
+DB_DATABASE='freeside'
+DB_DBA='freeside'
+DB_HOST='localhost'
+DB_PORT=''
+DB_RT_HOST='localhost'
+DB_RT_PASS=''
+DB_RT_USER='freeside'
+DB_TYPE='Pg'
+DEFS='-DPACKAGE_NAME=\"RT\" -DPACKAGE_TARNAME=\"rt\" -DPACKAGE_VERSION=\"3.6.4\" -DPACKAGE_STRING=\"RT\ 3.6.4\" -DPACKAGE_BUGREPORT=\"rt-bugs@bestpractical.com\" '
+DESTDIR='/opt/rt3'
+ECHO_C=''
+ECHO_N='-n'
+ECHO_T=''
+INSTALL_DATA='${INSTALL} -m 644'
+INSTALL_PROGRAM='${INSTALL}'
+INSTALL_SCRIPT='${INSTALL}'
+LIBOBJS=''
+LIBS=''
+LIBS_GROUP='bin'
+LIBS_OWNER='root'
+LOCAL_ETC_PATH='/opt/rt3/local/etc'
+LOCAL_LEXICON_PATH='/opt/rt3/local/po'
+LOCAL_LIB_PATH='/opt/rt3/local/lib'
+LTLIBOBJS=''
+MASON_DATA_PATH='/usr/local/etc/freeside/masondata'
+MASON_HTML_PATH='/var/www/freeside/rt'
+MASON_LOCAL_HTML_PATH='/opt/rt3/local/html'
+MASON_SESSION_PATH='/opt/rt3/var/session_data'
+PACKAGE_BUGREPORT='rt-bugs@bestpractical.com'
+PACKAGE_NAME='RT'
+PACKAGE_STRING='RT 3.6.4'
+PACKAGE_TARNAME='rt'
+PACKAGE_VERSION='3.6.4'
+PATH_SEPARATOR=':'
+PERL='/usr/bin/perl'
+RTGROUP='freeside'
+RT_BIN_PATH='/opt/rt3/bin'
+RT_DEVEL_MODE='0'
+RT_DOC_PATH='/opt/rt3/share/doc'
+RT_ETC_PATH='/opt/rt3/etc'
+RT_LIB_PATH='/opt/rt3/lib'
+RT_LOCAL_PATH='/opt/rt3/local'
+RT_LOG_PATH='/opt/rt3/var/log'
+RT_MAN_PATH='/opt/rt3/man'
+RT_PATH='/opt/rt3'
+RT_SBIN_PATH='/opt/rt3/sbin'
+RT_STANDALONE='0'
+RT_VAR_PATH='/opt/rt3/var'
+RT_VERSION_MAJOR='3'
+RT_VERSION_MINOR='6'
+RT_VERSION_PATCH='4'
+SHELL='/bin/sh'
+SPEEDY_BIN='/usr/local/bin/speedy'
+WEB_GROUP='freeside'
+WEB_USER='freeside'
+bindir='/opt/rt3/bin'
+build_alias=''
+customdir='/opt/rt3/local'
+custometcdir='/opt/rt3/local/etc'
+customhtmldir='/opt/rt3/local/html'
+customlexdir='/opt/rt3/local/po'
+customlibdir='/opt/rt3/local/lib'
+datadir='/opt/rt3/share'
+exec_prefix='/opt/rt3'
+exp_bindir='/opt/rt3/bin'
+exp_customdir='/opt/rt3/local'
+exp_custometcdir='/opt/rt3/local/etc'
+exp_customhtmldir='/opt/rt3/local/html'
+exp_customlexdir='/opt/rt3/local/po'
+exp_customlibdir='/opt/rt3/local/lib'
+exp_datadir='/opt/rt3/share'
+exp_exec_prefix='/opt/rt3'
+exp_htmldir='/var/www/freeside/rt'
+exp_libdir='/opt/rt3/lib'
+exp_localstatedir='/opt/rt3/var'
+exp_logfiledir='/opt/rt3/var/log'
+exp_mandir='/opt/rt3/man'
+exp_manualdir='/opt/rt3/share/doc'
+exp_masonstatedir='/usr/local/etc/freeside/masondata'
+exp_prefix='/opt/rt3'
+exp_sbindir='/opt/rt3/sbin'
+exp_sessionstatedir='/opt/rt3/var/session_data'
+exp_sysconfdir='/opt/rt3/etc'
+host_alias=''
+htmldir='/var/www/freeside/rt'
+includedir='${prefix}/include'
+infodir='${prefix}/info'
+libdir='/opt/rt3/lib'
+libexecdir='${exec_prefix}/libexec'
+localstatedir='/opt/rt3/var'
+logfiledir='/opt/rt3/var/log'
+mandir='/opt/rt3/man'
+manualdir='/opt/rt3/share/doc'
+masonstatedir='/usr/local/etc/freeside/masondata'
+oldincludedir='/usr/include'
+prefix='/opt/rt3'
+program_transform_name='s,x,x,'
+rt_layout_name='Freeside'
+rt_version_major='3'
+rt_version_minor='6'
+rt_version_patch='4'
+sbindir='/opt/rt3/sbin'
+sessionstatedir='/opt/rt3/var/session_data'
+sharedstatedir='${prefix}/com'
+sysconfdir='/opt/rt3/etc'
+target_alias=''
## ----------- ##
## confdefs.h. ##
## ----------- ##
+#define PACKAGE_BUGREPORT "rt-bugs@bestpractical.com"
#define PACKAGE_NAME "RT"
+#define PACKAGE_STRING "RT 3.6.4"
#define PACKAGE_TARNAME "rt"
-#define PACKAGE_VERSION "3.0.9"
-#define PACKAGE_STRING "RT 3.0.9"
-#define PACKAGE_BUGREPORT "rt-3.0-bugs@fsck.com"
+#define PACKAGE_VERSION "3.6.4"
configure: exit 0
diff --git a/rt/config.pld b/rt/config.pld
deleted file mode 100644
index c83c28fdb..000000000
--- a/rt/config.pld
+++ /dev/null
@@ -1,20 +0,0 @@
-(test "x$prefix" = "xNONE" || test "x$prefix" = "x") && prefix=/opt/rt3
-(test "x$exec_prefix" = "xNONE" || test "x$exec_prefix" = "x") && exec_prefix=${prefix}
-bindir=bin
-sbindir=sbin
-sysconfdir=etc
-mandir=man
-(test "x$plugindir" = "xNONE" || test "x$plugindir" = "x") && plugindir=plugins
-libdir=lib
-datadir=share
-htmldir=${datadir}/html
-(test "x$manualdir" = "xNONE" || test "x$manualdir" = "x") && manualdir=${datadir}/doc
-localstatedir=var
-(test "x$logfiledir" = "xNONE" || test "x$logfiledir" = "x") && logfiledir=${localstatedir}/log
-(test "x$masonstatedir" = "xNONE" || test "x$masonstatedir" = "x") && masonstatedir=${localstatedir}/mason_data
-(test "x$sessionstatedir" = "xNONE" || test "x$sessionstatedir" = "x") && sessionstatedir=${localstatedir}/session_data
-(test "x$customdir" = "xNONE" || test "x$customdir" = "x") && customdir=local
-(test "x$custometcdir" = "xNONE" || test "x$custometcdir" = "x") && custometcdir=${customdir}/etc
-(test "x$customhtmldir" = "xNONE" || test "x$customhtmldir" = "x") && customhtmldir=${customdir}/html
-(test "x$customlexdir" = "xNONE" || test "x$customlexdir" = "x") && customlexdir=${customdir}/po
-(test "x$customlibdir" = "xNONE" || test "x$customlibdir" = "x") && customlibdir=${customdir}/lib
diff --git a/rt/config.status b/rt/config.status
index 9c286b565..06c562a7d 100755
--- a/rt/config.status
+++ b/rt/config.status
@@ -7,250 +7,78 @@
debug=false
ac_cs_recheck=false
ac_cs_silent=false
-
SHELL=${CONFIG_SHELL-/bin/sh}
-export SHELL
-## -------------------- ##
-## M4sh Initialization. ##
-## -------------------- ##
+## --------------------- ##
+## M4sh Initialization. ##
+## --------------------- ##
-# Be more Bourne compatible
-DUALCASE=1; export DUALCASE # for MKS sh
-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
emulate sh
NULLCMD=:
- # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
- setopt NO_GLOB_SUBST
-else
- case `(set -o) 2>/dev/null` in #(
- *posix*) :
- set -o posix ;; #(
- *) :
- ;;
-esac
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+ set -o posix
fi
+DUALCASE=1; export DUALCASE # for MKS sh
-
-as_nl='
-'
-export as_nl
-# Printing a long string crashes Solaris 7 /usr/bin/printf.
-as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
-# Prefer a ksh shell builtin over an external printf program on Solaris,
-# but without wasting forks for bash or zsh.
-if test -z "$BASH_VERSION$ZSH_VERSION" \
- && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
- as_echo='print -r --'
- as_echo_n='print -rn --'
-elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
- as_echo='printf %s\n'
- as_echo_n='printf %s'
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ as_unset=unset
else
- if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
- as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
- as_echo_n='/usr/ucb/echo -n'
- else
- as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
- as_echo_n_body='eval
- arg=$1;
- case $arg in #(
- *"$as_nl"*)
- expr "X$arg" : "X\\(.*\\)$as_nl";
- arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
- esac;
- expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
- '
- export as_echo_n_body
- as_echo_n='sh -c $as_echo_n_body as_echo'
- fi
- export as_echo_body
- as_echo='sh -c $as_echo_body as_echo'
-fi
-
-# The user is always right.
-if test "${PATH_SEPARATOR+set}" != set; then
- PATH_SEPARATOR=:
- (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
- (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
- PATH_SEPARATOR=';'
- }
+ as_unset=false
fi
-# IFS
-# We need space, tab and new line, in precisely that order. Quoting is
-# there to prevent editors from complaining about space-tab.
-# (If _AS_PATH_WALK were called with IFS unset, it would disable word
-# splitting by setting IFS to empty value.)
-IFS=" "" $as_nl"
-
-# Find who we are. Look in the path if we contain no directory separator.
-case $0 in #((
- *[\\/]* ) as_myself=$0 ;;
- *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
- done
-IFS=$as_save_IFS
-
- ;;
-esac
-# We did not find ourselves, most probably we were run as `sh COMMAND'
-# in which case we are not to be found in the path.
-if test "x$as_myself" = x; then
- as_myself=$0
-fi
-if test ! -f "$as_myself"; then
- $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
- exit 1
-fi
-
-# Unset variables that we do not need and which cause bugs (e.g. in
-# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
-# suppresses any "Segmentation fault" message there. '((' could
-# trigger a bug in pdksh 5.2.14.
-for as_var in BASH_ENV ENV MAIL MAILPATH
-do eval test x\${$as_var+set} = xset \
- && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
-done
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
PS1='$ '
PS2='> '
PS4='+ '
# NLS nuisances.
-LC_ALL=C
-export LC_ALL
-LANGUAGE=C
-export LANGUAGE
-
-# CDPATH.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-
-
-# as_fn_error ERROR [LINENO LOG_FD]
-# ---------------------------------
-# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
-# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
-# script with status $?, using 1 if that was 0.
-as_fn_error ()
-{
- as_status=$?; test $as_status -eq 0 && as_status=1
- if test "$3"; then
- as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3
+for as_var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+ eval $as_var=C; export $as_var
+ else
+ $as_unset $as_var
fi
- $as_echo "$as_me: error: $1" >&2
- as_fn_exit $as_status
-} # as_fn_error
-
-
-# as_fn_set_status STATUS
-# -----------------------
-# Set $? to STATUS, without forking.
-as_fn_set_status ()
-{
- return $1
-} # as_fn_set_status
-
-# as_fn_exit STATUS
-# -----------------
-# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
-as_fn_exit ()
-{
- set +e
- as_fn_set_status $1
- exit $1
-} # as_fn_exit
-
-# as_fn_unset VAR
-# ---------------
-# Portably unset VAR.
-as_fn_unset ()
-{
- { eval $1=; unset $1;}
-}
-as_unset=as_fn_unset
-# as_fn_append VAR VALUE
-# ----------------------
-# Append the text in VALUE to the end of the definition contained in VAR. Take
-# advantage of any shell optimizations that allow amortized linear growth over
-# repeated appends, instead of the typical quadratic growth present in naive
-# implementations.
-if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
- eval 'as_fn_append ()
- {
- eval $1+=\$2
- }'
-else
- as_fn_append ()
- {
- eval $1=\$$1\$2
- }
-fi # as_fn_append
-
-# as_fn_arith ARG...
-# ------------------
-# Perform arithmetic evaluation on the ARGs, and store the result in the
-# global $as_val. Take advantage of shells that can avoid forks. The arguments
-# must be portable across $(()) and expr.
-if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
- eval 'as_fn_arith ()
- {
- as_val=$(( $* ))
- }'
-else
- as_fn_arith ()
- {
- as_val=`expr "$@" || test $? -eq 1`
- }
-fi # as_fn_arith
-
+done
-if expr a : '\(a\)' >/dev/null 2>&1 &&
- test "X`expr 00001 : '.*\(...\)'`" = X001; then
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
as_expr=expr
else
as_expr=false
fi
-if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
as_basename=basename
else
as_basename=false
fi
-if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
- as_dirname=dirname
-else
- as_dirname=false
-fi
-as_me=`$as_basename -- "$0" ||
+# Name of the executable.
+as_me=`$as_basename "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
X"$0" : 'X\(//\)$' \| \
- X"$0" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X/"$0" |
- sed '/^.*\/\([^/][^/]*\)\/*$/{
- s//\1/
- q
- }
- /^X\/\(\/\/\)$/{
- s//\1/
- q
- }
- /^X\/\(\/\).*/{
- s//\1/
- q
- }
- s/.*/./; q'`
+ X"$0" : 'X\(/\)$' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+ /^X\/\(\/\/\)$/{ s//\1/; q; }
+ /^X\/\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+
+# PATH needs CR, and LINENO needs CR and PATH.
# Avoid depending upon Character Ranges.
as_cr_letters='abcdefghijklmnopqrstuvwxyz'
as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
@@ -258,123 +86,148 @@ as_cr_Letters=$as_cr_letters$as_cr_LETTERS
as_cr_digits='0123456789'
as_cr_alnum=$as_cr_Letters$as_cr_digits
-ECHO_C= ECHO_N= ECHO_T=
-case `echo -n x` in #(((((
--n*)
- case `echo 'xy\c'` in
- *c*) ECHO_T=' ';; # ECHO_T is single tab character.
- xy) ECHO_C='\c';;
- *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
- ECHO_T=' ';;
- esac;;
-*)
- ECHO_N='-n';;
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ echo "#! /bin/sh" >conf$$.sh
+ echo "exit 0" >>conf$$.sh
+ chmod +x conf$$.sh
+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+ PATH_SEPARATOR=';'
+ else
+ PATH_SEPARATOR=:
+ fi
+ rm -f conf$$.sh
+fi
+
+
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" || {
+ # Find who we are. Look in the path if we contain no path at all
+ # relative or not.
+ case $0 in
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+ ;;
+ esac
+ # We did not find ourselves, most probably we were run as `sh COMMAND'
+ # in which case we are not to be found in the path.
+ if test "x$as_myself" = x; then
+ as_myself=$0
+ fi
+ if test ! -f "$as_myself"; then
+ { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+ { (exit 1); exit 1; }; }
+ fi
+ case $CONFIG_SHELL in
+ '')
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for as_base in sh bash ksh sh5; do
+ case $as_dir in
+ /*)
+ if ("$as_dir/$as_base" -c '
+ as_lineno_1=$LINENO
+ as_lineno_2=$LINENO
+ as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+ test "x$as_lineno_1" != "x$as_lineno_2" &&
+ test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then
+ $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+ $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+ CONFIG_SHELL=$as_dir/$as_base
+ export CONFIG_SHELL
+ exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+ fi;;
+ esac
+ done
+done
+;;
+ esac
+
+ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+ # uniformly replaced by the line number. The first 'sed' inserts a
+ # line-number line before each line; the second 'sed' does the real
+ # work. The second script uses 'N' to pair each line-number line
+ # with the numbered line, and appends trailing '-' during
+ # substitution so that $LINENO is not a special case at line end.
+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+ # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-)
+ sed '=' <$as_myself |
+ sed '
+ N
+ s,$,-,
+ : loop
+ s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+ t loop
+ s,-$,,
+ s,^['$as_cr_digits']*\n,,
+ ' >$as_me.lineno &&
+ chmod +x $as_me.lineno ||
+ { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+ { (exit 1); exit 1; }; }
+
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensible to this).
+ . ./$as_me.lineno
+ # Exit status is that of the last command.
+ exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+ *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T=' ' ;;
+ *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+ *) ECHO_N= ECHO_C='\c' ECHO_T= ;;
esac
-rm -f conf$$ conf$$.exe conf$$.file
-if test -d conf$$.dir; then
- rm -f conf$$.dir/conf$$.file
+if expr a : '\(a\)' >/dev/null 2>&1; then
+ as_expr=expr
else
- rm -f conf$$.dir
- mkdir conf$$.dir 2>/dev/null
+ as_expr=false
fi
-if (echo >conf$$.file) 2>/dev/null; then
- if ln -s conf$$.file conf$$ 2>/dev/null; then
- as_ln_s='ln -s'
- # ... but there are two gotchas:
- # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
- # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
- # In both cases, we have to default to `cp -p'.
- ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
- as_ln_s='cp -p'
- elif ln conf$$.file conf$$ 2>/dev/null; then
- as_ln_s=ln
- else
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+ # We could just check for DJGPP; but this test a) works b) is more generic
+ # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+ if test -f conf$$.exe; then
+ # Don't use ln at all; we don't have any links
as_ln_s='cp -p'
+ else
+ as_ln_s='ln -s'
fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
else
as_ln_s='cp -p'
fi
-rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
-rmdir conf$$.dir 2>/dev/null
-
-
-# as_fn_mkdir_p
-# -------------
-# Create "$as_dir" as a directory, including parents if necessary.
-as_fn_mkdir_p ()
-{
-
- case $as_dir in #(
- -*) as_dir=./$as_dir;;
- esac
- test -d "$as_dir" || eval $as_mkdir_p || {
- as_dirs=
- while :; do
- case $as_dir in #(
- *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
- *) as_qdir=$as_dir;;
- esac
- as_dirs="'$as_qdir' $as_dirs"
- as_dir=`$as_dirname -- "$as_dir" ||
-$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
- X"$as_dir" : 'X\(//\)[^/]' \| \
- X"$as_dir" : 'X\(//\)$' \| \
- X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$as_dir" |
- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
- s//\1/
- q
- }
- /^X\(\/\/\)[^/].*/{
- s//\1/
- q
- }
- /^X\(\/\/\)$/{
- s//\1/
- q
- }
- /^X\(\/\).*/{
- s//\1/
- q
- }
- s/.*/./; q'`
- test -d "$as_dir" && break
- done
- test -z "$as_dirs" || eval "mkdir $as_dirs"
- } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir"
-
+rm -f conf$$ conf$$.exe conf$$.file
-} # as_fn_mkdir_p
if mkdir -p . 2>/dev/null; then
- as_mkdir_p='mkdir -p "$as_dir"'
+ as_mkdir_p=:
else
test -d ./-p && rmdir ./-p
as_mkdir_p=false
fi
-if test -x / >/dev/null 2>&1; then
- as_test_x='test -x'
-else
- if ls -dL / >/dev/null 2>&1; then
- as_ls_L_option=L
- else
- as_ls_L_option=
- fi
- as_test_x='
- eval sh -c '\''
- if test -d "$1"; then
- test -d "$1/.";
- else
- case $1 in #(
- -*)set "./$1";;
- esac;
- case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
- ???[sx]*):;;*)false;;esac;fi
- '\'' sh
- '
-fi
-as_executable_p=$as_test_x
+as_executable_p="test -f"
# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
@@ -383,16 +236,31 @@ as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS=" $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
exec 6>&1
-## ----------------------------------- ##
-## Main body of $CONFIG_STATUS script. ##
-## ----------------------------------- ##
-# Save the log message, to keep $0 and so on meaningful, and to
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
# report actual input values of CONFIG_FILES etc. instead of their
-# values after options handling.
-ac_log="
-This file was extended by RT $as_me 3.8.7, which was
-generated by GNU Autoconf 2.64. Invocation command line was
+# values after options handling. Logging --version etc. is OK.
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by RT $as_me 3.6.4, which was
+generated by GNU Autoconf 2.59. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
@@ -400,91 +268,98 @@ generated by GNU Autoconf 2.64. Invocation command line was
CONFIG_COMMANDS = $CONFIG_COMMANDS
$ $0 $@
-on `(hostname || uname -n) 2>/dev/null | sed 1q`
-"
-
-# Files that config.status was made for.
-config_files=" etc/upgrade/3.8-branded-queues-extension etc/upgrade/3.8-ical-extension etc/upgrade/split-out-cf-categories sbin/rt-attributes-viewer sbin/rt-dump-database sbin/rt-setup-database sbin/rt-test-dependencies sbin/rt-email-digest sbin/rt-email-dashboards sbin/rt-clean-sessions sbin/rt-shredder sbin/rt-validator sbin/rt-email-group-admin sbin/rt-server bin/mason_handler.fcgi bin/mason_handler.scgi bin/standalone_httpd bin/rt-crontool bin/rt-mailgate bin/rt Makefile etc/RT_Config.pm lib/RT.pm bin/mason_handler.svc bin/webmux.pl t/data/configs/apache2.2+mod_perl.conf t/data/configs/apache2.2+fastcgi.conf"
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+config_files=" sbin/rt-dump-database sbin/rt-setup-database sbin/rt-test-dependencies bin/mason_handler.fcgi bin/mason_handler.scgi bin/standalone_httpd bin/rt-crontool bin/rt-mailgate bin/rt Makefile etc/RT_Config.pm lib/RT.pm bin/mason_handler.svc bin/webmux.pl"
ac_cs_usage="\
-\`$as_me' instantiates files and other configuration actions
-from templates according to the current configuration. Unless the files
-and actions are specified as TAGs, all are instantiated by default.
+\`$as_me' instantiates files from templates according to the
+current configuration.
-Usage: $0 [OPTION]... [TAG]...
+Usage: $0 [OPTIONS] [FILE]...
-h, --help print this help, then exit
- -V, --version print version number and configuration settings, then exit
- -q, --quiet, --silent
- do not print progress messages
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
-d, --debug don't remove temporary files
--recheck update $as_me by reconfiguring in the same conditions
- --file=FILE[:TEMPLATE]
- instantiate the configuration file FILE
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
Configuration files:
$config_files
-Report bugs to <rt-bugs@bestpractical.com>."
-
+Report bugs to <bug-autoconf@gnu.org>."
ac_cs_version="\
-RT config.status 3.8.7
-configured by ./configure, generated by GNU Autoconf 2.64,
- with options \"'--with-db-type=SQLite' '--enable-layout=relative' '--with-web-handler=standalone' 'PERL=/usr/bin/perl'\"
+RT config.status 3.6.4
+configured by ./configure, generated by GNU Autoconf 2.59,
+ with options \"'--enable-layout=Freeside' '--with-db-type=Pg' '--with-db-dba=freeside' '--with-db-database=freeside' '--with-db-rt-user=freeside' '--with-db-rt-pass=' '--with-web-user=freeside' '--with-web-group=freeside' '--with-rt-group=freeside'\"
-Copyright (C) 2009 Free Software Foundation, Inc.
+Copyright (C) 2003 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."
-
-ac_pwd='/Users/falcone/work/rt/releases/rt-3.8.7'
-srcdir='.'
-INSTALL='install-sh'
-AWK='gawk'
-test -n "$AWK" || AWK=awk
-# The default lists apply if the user does not specify any file.
+srcdir=.
+INSTALL="/usr/bin/install -c"
+# If no file are specified by the user, then we need to provide default
+# value. By we need to know if files were specified by the user.
ac_need_defaults=:
while test $# != 0
do
case $1 in
--*=*)
- ac_option=`expr "X$1" : 'X\([^=]*\)='`
- ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_option=`expr "x$1" : 'x\([^=]*\)='`
+ ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
ac_shift=:
;;
- *)
+ -*)
ac_option=$1
ac_optarg=$2
ac_shift=shift
;;
+ *) # This is not an option, so the user has probably given explicit
+ # arguments.
+ ac_option=$1
+ ac_need_defaults=false;;
esac
case $ac_option in
# Handling of the options.
-recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
ac_cs_recheck=: ;;
- --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
- $as_echo "$ac_cs_version"; exit ;;
- --debug | --debu | --deb | --de | --d | -d )
+ --version | --vers* | -V )
+ echo "$ac_cs_version"; exit 0 ;;
+ --he | --h)
+ # Conflict between --help and --header
+ { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; };;
+ --help | --hel | -h )
+ echo "$ac_cs_usage"; exit 0 ;;
+ --debug | --d* | -d )
debug=: ;;
--file | --fil | --fi | --f )
$ac_shift
- case $ac_optarg in
- *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
- esac
- as_fn_append CONFIG_FILES " '$ac_optarg'"
+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
ac_need_defaults=false;;
- --he | --h | --help | --hel | -h )
- $as_echo "$ac_cs_usage"; exit ;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil | --si | --s)
ac_cs_silent=: ;;
# This is an error.
- -*) as_fn_error "unrecognized option: \`$1'
-Try \`$0 --help' for more information." ;;
+ -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+ { (exit 1); exit 1; }; } ;;
- *) as_fn_append ac_config_targets " $1"
- ac_need_defaults=false ;;
+ *) ac_config_targets="$ac_config_targets $1" ;;
esac
shift
@@ -498,61 +373,34 @@ if $ac_cs_silent; then
fi
if $ac_cs_recheck; then
- set X '/bin/sh' './configure' '--with-db-type=SQLite' '--enable-layout=relative' '--with-web-handler=standalone' 'PERL=/usr/bin/perl' $ac_configure_extra_args --no-create --no-recursion
- shift
- $as_echo "running CONFIG_SHELL=/bin/sh $*" >&6
- CONFIG_SHELL='/bin/sh'
- export CONFIG_SHELL
- exec "$@"
+ echo "running /bin/sh ./configure " '--enable-layout=Freeside' '--with-db-type=Pg' '--with-db-dba=freeside' '--with-db-database=freeside' '--with-db-rt-user=freeside' '--with-db-rt-pass=' '--with-web-user=freeside' '--with-web-group=freeside' '--with-rt-group=freeside' $ac_configure_extra_args " --no-create --no-recursion" >&6
+ exec /bin/sh ./configure '--enable-layout=Freeside' '--with-db-type=Pg' '--with-db-dba=freeside' '--with-db-database=freeside' '--with-db-rt-user=freeside' '--with-db-rt-pass=' '--with-web-user=freeside' '--with-web-group=freeside' '--with-rt-group=freeside' $ac_configure_extra_args --no-create --no-recursion
fi
-exec 5>>config.log
-{
- echo
- sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
-## Running $as_me. ##
-_ASBOX
- $as_echo "$ac_log"
-} >&5
-
-
-# Handling of arguments.
for ac_config_target in $ac_config_targets
do
- case $ac_config_target in
- "etc/upgrade/3.8-branded-queues-extension") CONFIG_FILES="$CONFIG_FILES etc/upgrade/3.8-branded-queues-extension" ;;
- "etc/upgrade/3.8-ical-extension") CONFIG_FILES="$CONFIG_FILES etc/upgrade/3.8-ical-extension" ;;
- "etc/upgrade/split-out-cf-categories") CONFIG_FILES="$CONFIG_FILES etc/upgrade/split-out-cf-categories" ;;
- "sbin/rt-attributes-viewer") CONFIG_FILES="$CONFIG_FILES sbin/rt-attributes-viewer" ;;
- "sbin/rt-dump-database") CONFIG_FILES="$CONFIG_FILES sbin/rt-dump-database" ;;
- "sbin/rt-setup-database") CONFIG_FILES="$CONFIG_FILES sbin/rt-setup-database" ;;
- "sbin/rt-test-dependencies") CONFIG_FILES="$CONFIG_FILES sbin/rt-test-dependencies" ;;
- "sbin/rt-email-digest") CONFIG_FILES="$CONFIG_FILES sbin/rt-email-digest" ;;
- "sbin/rt-email-dashboards") CONFIG_FILES="$CONFIG_FILES sbin/rt-email-dashboards" ;;
- "sbin/rt-clean-sessions") CONFIG_FILES="$CONFIG_FILES sbin/rt-clean-sessions" ;;
- "sbin/rt-shredder") CONFIG_FILES="$CONFIG_FILES sbin/rt-shredder" ;;
- "sbin/rt-validator") CONFIG_FILES="$CONFIG_FILES sbin/rt-validator" ;;
- "sbin/rt-email-group-admin") CONFIG_FILES="$CONFIG_FILES sbin/rt-email-group-admin" ;;
- "sbin/rt-server") CONFIG_FILES="$CONFIG_FILES sbin/rt-server" ;;
- "bin/mason_handler.fcgi") CONFIG_FILES="$CONFIG_FILES bin/mason_handler.fcgi" ;;
- "bin/mason_handler.scgi") CONFIG_FILES="$CONFIG_FILES bin/mason_handler.scgi" ;;
- "bin/standalone_httpd") CONFIG_FILES="$CONFIG_FILES bin/standalone_httpd" ;;
- "bin/rt-crontool") CONFIG_FILES="$CONFIG_FILES bin/rt-crontool" ;;
- "bin/rt-mailgate") CONFIG_FILES="$CONFIG_FILES bin/rt-mailgate" ;;
- "bin/rt") CONFIG_FILES="$CONFIG_FILES bin/rt" ;;
- "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
- "etc/RT_Config.pm") CONFIG_FILES="$CONFIG_FILES etc/RT_Config.pm" ;;
- "lib/RT.pm") CONFIG_FILES="$CONFIG_FILES lib/RT.pm" ;;
- "bin/mason_handler.svc") CONFIG_FILES="$CONFIG_FILES bin/mason_handler.svc" ;;
- "bin/webmux.pl") CONFIG_FILES="$CONFIG_FILES bin/webmux.pl" ;;
- "t/data/configs/apache2.2+mod_perl.conf") CONFIG_FILES="$CONFIG_FILES t/data/configs/apache2.2+mod_perl.conf" ;;
- "t/data/configs/apache2.2+fastcgi.conf") CONFIG_FILES="$CONFIG_FILES t/data/configs/apache2.2+fastcgi.conf" ;;
-
- *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ case "$ac_config_target" in
+ # Handling of arguments.
+ "sbin/rt-dump-database" ) CONFIG_FILES="$CONFIG_FILES sbin/rt-dump-database" ;;
+ "sbin/rt-setup-database" ) CONFIG_FILES="$CONFIG_FILES sbin/rt-setup-database" ;;
+ "sbin/rt-test-dependencies" ) CONFIG_FILES="$CONFIG_FILES sbin/rt-test-dependencies" ;;
+ "bin/mason_handler.fcgi" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.fcgi" ;;
+ "bin/mason_handler.scgi" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.scgi" ;;
+ "bin/standalone_httpd" ) CONFIG_FILES="$CONFIG_FILES bin/standalone_httpd" ;;
+ "bin/rt-crontool" ) CONFIG_FILES="$CONFIG_FILES bin/rt-crontool" ;;
+ "bin/rt-mailgate" ) CONFIG_FILES="$CONFIG_FILES bin/rt-mailgate" ;;
+ "bin/rt" ) CONFIG_FILES="$CONFIG_FILES bin/rt" ;;
+ "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "etc/RT_Config.pm" ) CONFIG_FILES="$CONFIG_FILES etc/RT_Config.pm" ;;
+ "lib/RT.pm" ) CONFIG_FILES="$CONFIG_FILES lib/RT.pm" ;;
+ "bin/mason_handler.svc" ) CONFIG_FILES="$CONFIG_FILES bin/mason_handler.svc" ;;
+ "bin/webmux.pl" ) CONFIG_FILES="$CONFIG_FILES bin/webmux.pl" ;;
+ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+ { (exit 1); exit 1; }; };;
esac
done
-
# If the user did not use the arguments to specify the items to instantiate,
# then the envvar interface is used. Set only those that are not.
# We use the long form for the default assignment because of an extremely
@@ -562,495 +410,408 @@ if $ac_need_defaults; then
fi
# Have a temporary directory for convenience. Make it in the build tree
-# simply because there is no reason against having it here, and in addition,
+# simply because there is no reason to put it here, and in addition,
# creating and moving files from /tmp can sometimes cause problems.
-# Hook for its removal unless debugging.
-# Note that there is a small window in which the directory will not be cleaned:
-# after its creation but before its name has been assigned to `$tmp'.
+# Create a temporary directory, and hook for its removal unless debugging.
$debug ||
{
- tmp=
- trap 'exit_status=$?
- { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
-' 0
- trap 'as_fn_exit 1' 1 2 13 15
+ trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+ trap '{ (exit 1); exit 1; }' 1 2 13 15
}
+
# Create a (secure) tmp directory for tmp files.
{
- tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
test -n "$tmp" && test -d "$tmp"
} ||
{
- tmp=./conf$$-$RANDOM
- (umask 077 && mkdir "$tmp")
-} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5
-
-# Set up the scripts for CONFIG_FILES section.
-# No need to generate them if there are no CONFIG_FILES.
-# This happens for instance with `./config.status config.h'.
-if test -n "$CONFIG_FILES"; then
-
-
-ac_cr=`echo X | tr X '\015'`
-# On cygwin, bash can eat \r inside `` if the user requested igncr.
-# But we know of no other shell where ac_cr would be empty at this
-# point, so we can use a bashism as a fallback.
-if test "x$ac_cr" = x; then
- eval ac_cr=\$\'\\r\'
-fi
-ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
-if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
- ac_cs_awk_cr='\r'
-else
- ac_cs_awk_cr=$ac_cr
-fi
-
-echo 'BEGIN {' >"$tmp/subs1.awk" &&
-cat >>"$tmp/subs1.awk" <<\_ACAWK &&
-S["LTLIBOBJS"]=""
-S["LIBOBJS"]=""
-S["RT_LOG_PATH_R"]="/opt/rt3/var/log"
-S["LOCAL_LIB_PATH_R"]="/opt/rt3/local/lib"
-S["LOCAL_LEXICON_PATH_R"]="/opt/rt3/local/po"
-S["MASON_LOCAL_HTML_PATH_R"]="/opt/rt3/local/html"
-S["LOCAL_ETC_PATH_R"]="/opt/rt3/local/etc"
-S["MASON_HTML_PATH_R"]="/opt/rt3/share/html"
-S["MASON_SESSION_PATH_R"]="/opt/rt3/var/session_data"
-S["MASON_DATA_PATH_R"]="/opt/rt3/var/mason_data"
-S["RT_PLUGIN_PATH_R"]="/opt/rt3/plugins"
-S["RT_MAN_PATH_R"]="/opt/rt3/man"
-S["RT_VAR_PATH_R"]="/opt/rt3/var"
-S["RT_SBIN_PATH_R"]="/opt/rt3/sbin"
-S["RT_BIN_PATH_R"]="/opt/rt3/bin"
-S["CONFIG_FILE_PATH_R"]="/opt/rt3/etc"
-S["RT_ETC_PATH_R"]="/opt/rt3/etc"
-S["RT_LIB_PATH_R"]="/opt/rt3/lib"
-S["RT_LOCAL_PATH_R"]="/opt/rt3/local"
-S["RT_DOC_PATH_R"]="/opt/rt3/share/doc"
-S["RT_PATH_R"]="/opt/rt3"
-S["RT_LOG_PATH"]="var/log"
-S["LOCAL_LIB_PATH"]="local/lib"
-S["LOCAL_LEXICON_PATH"]="local/po"
-S["MASON_LOCAL_HTML_PATH"]="local/html"
-S["LOCAL_ETC_PATH"]="local/etc"
-S["MASON_HTML_PATH"]="share/html"
-S["MASON_SESSION_PATH"]="var/session_data"
-S["MASON_DATA_PATH"]="var/mason_data"
-S["RT_PLUGIN_PATH"]="plugins"
-S["RT_MAN_PATH"]="man"
-S["RT_VAR_PATH"]="var"
-S["RT_SBIN_PATH"]="sbin"
-S["RT_BIN_PATH"]="bin"
-S["CONFIG_FILE_PATH"]="etc"
-S["RT_ETC_PATH"]="etc"
-S["RT_LIB_PATH"]="lib"
-S["RT_LOCAL_PATH"]="local"
-S["RT_DOC_PATH"]="share/doc"
-S["RT_PATH"]="/opt/rt3"
-S["RT_VERSION_PATCH"]="7"
-S["RT_VERSION_MINOR"]="8"
-S["RT_VERSION_MAJOR"]="3"
-S["RT_GPG"]="1"
-S["RT_GD"]="1"
-S["RT_GRAPHVIZ"]="0"
-S["OBJEXT"]="o"
-S["EXEEXT"]=""
-S["ac_ct_CC"]="gcc"
-S["CPPFLAGS"]=""
-S["LDFLAGS"]=""
-S["CFLAGS"]="-g -O2"
-S["CC"]="gcc"
-S["RT_DEVEL_MODE"]="0"
-S["APACHECTL"]="/usr/sbin/apachectl"
-S["RTGROUP"]="www"
-S["WEB_GROUP"]="www"
-S["WEB_USER"]="www"
-S["DB_RT_PASS"]="rt_pass"
-S["DB_RT_USER"]="rt_user"
-S["DB_DATABASE"]="rt3"
-S["DB_DBA"]="root"
-S["DB_RT_HOST"]="localhost"
-S["DB_PORT"]=""
-S["DB_HOST"]="localhost"
-S["DATABASE_ENV_PREF"]=""
-S["DB_TYPE"]="SQLite"
-S["LIBS_GROUP"]="bin"
-S["LIBS_OWNER"]="root"
-S["BIN_OWNER"]="root"
-S["COMMENT_INPLACE_LAYOUT"]=""
-S["rt_layout_name"]="relative"
-S["exp_customlibdir"]="local/lib"
-S["customlibdir"]="local/lib"
-S["exp_customlexdir"]="local/po"
-S["customlexdir"]="local/po"
-S["exp_customhtmldir"]="local/html"
-S["customhtmldir"]="local/html"
-S["exp_custometcdir"]="local/etc"
-S["custometcdir"]="local/etc"
-S["exp_customdir"]="local"
-S["customdir"]="local"
-S["exp_sessionstatedir"]="var/session_data"
-S["sessionstatedir"]="var/session_data"
-S["exp_masonstatedir"]="var/mason_data"
-S["masonstatedir"]="var/mason_data"
-S["exp_logfiledir"]="var/log"
-S["logfiledir"]="var/log"
-S["exp_localstatedir"]="var"
-S["exp_plugindir"]="plugins"
-S["plugindir"]="plugins"
-S["exp_manualdir"]="share/doc"
-S["manualdir"]="share/doc"
-S["exp_htmldir"]="share/html"
-S["exp_datadir"]="share"
-S["exp_libdir"]="lib"
-S["exp_mandir"]="man"
-S["exp_sysconfdir"]="etc"
-S["exp_sbindir"]="sbin"
-S["exp_bindir"]="bin"
-S["exp_exec_prefix"]="/opt/rt3"
-S["exp_prefix"]="/opt/rt3"
-S["SPEEDY_BIN"]="/usr/local/bin/speedy"
-S["WEB_HANDLER"]="standalone"
-S["PERL"]="/usr/bin/perl"
-S["AWK"]="gawk"
-S["INSTALL_DATA"]="${INSTALL} -m 644"
-S["INSTALL_SCRIPT"]="${INSTALL}"
-S["INSTALL_PROGRAM"]="${INSTALL}"
-S["rt_version_patch"]="7"
-S["rt_version_minor"]="8"
-S["rt_version_major"]="3"
-S["target_alias"]=""
-S["host_alias"]=""
-S["build_alias"]=""
-S["LIBS"]=""
-S["ECHO_T"]=""
-S["ECHO_N"]=""
-S["ECHO_C"]="\\c"
-S["DEFS"]="-DPACKAGE_NAME=\\\"RT\\\" -DPACKAGE_TARNAME=\\\"rt\\\" -DPACKAGE_VERSION=\\\"3.8.7\\\" -DPACKAGE_STRING=\\\"RT\\ 3.8.7\\\" -DPACKAGE_BUGREPORT=\\\"rt-bugs@bestpractica"\
-"l.com\\\" -DPACKAGE_URL=\\\"\\\""
-S["mandir"]="man"
-S["localedir"]="${datarootdir}/locale"
-S["libdir"]="lib"
-S["psdir"]="${docdir}"
-S["pdfdir"]="${docdir}"
-S["dvidir"]="${docdir}"
-S["htmldir"]="share/html"
-S["infodir"]="${datarootdir}/info"
-S["docdir"]="${datarootdir}/doc/${PACKAGE_TARNAME}"
-S["oldincludedir"]="/usr/include"
-S["includedir"]="${prefix}/include"
-S["localstatedir"]="var"
-S["sharedstatedir"]="${prefix}/com"
-S["sysconfdir"]="etc"
-S["datadir"]="share"
-S["datarootdir"]="${prefix}/share"
-S["libexecdir"]="${exec_prefix}/libexec"
-S["sbindir"]="sbin"
-S["bindir"]="bin"
-S["program_transform_name"]="s,x,x,"
-S["prefix"]="/opt/rt3"
-S["exec_prefix"]="/opt/rt3"
-S["PACKAGE_URL"]=""
-S["PACKAGE_BUGREPORT"]="rt-bugs@bestpractical.com"
-S["PACKAGE_STRING"]="RT 3.8.7"
-S["PACKAGE_VERSION"]="3.8.7"
-S["PACKAGE_TARNAME"]="rt"
-S["PACKAGE_NAME"]="RT"
-S["PATH_SEPARATOR"]=":"
-S["SHELL"]="/bin/sh"
-_ACAWK
-cat >>"$tmp/subs1.awk" <<_ACAWK &&
- for (key in S) S_is_set[key] = 1
- FS = ""
-
-}
+ tmp=./confstat$$-$RANDOM
+ (umask 077 && mkdir $tmp)
+} ||
{
- line = $ 0
- nfields = split(line, field, "@")
- substed = 0
- len = length(field[1])
- for (i = 2; i < nfields; i++) {
- key = field[i]
- keylen = length(key)
- if (S_is_set[key]) {
- value = S[key]
- line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
- len += length(value) + length(field[++i])
- substed = 1
- } else
- len += 1 + keylen
- }
-
- print line
+ echo "$me: cannot create a temporary directory in ." >&2
+ { (exit 1); exit 1; }
}
-_ACAWK
-if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
- sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
-else
- cat
-fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
- || as_fn_error "could not setup config files machinery" "$LINENO" 5
-fi # test -n "$CONFIG_FILES"
+#
+# CONFIG_FILES section.
+#
-eval set X " :F $CONFIG_FILES "
-shift
-for ac_tag
-do
- case $ac_tag in
- :[FHLC]) ac_mode=$ac_tag; continue;;
- esac
- case $ac_mode$ac_tag in
- :[FHL]*:*);;
- :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;;
- :[FH]-) ac_tag=-:-;;
- :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
- esac
- ac_save_IFS=$IFS
- IFS=:
- set x $ac_tag
- IFS=$ac_save_IFS
- shift
- ac_file=$1
- shift
-
- case $ac_mode in
- :L) ac_source=$1;;
- :[FH])
- ac_file_inputs=
- for ac_f
- do
- case $ac_f in
- -) ac_f="$tmp/stdin";;
- *) # Look for the file first in the build tree, then in the source tree
- # (if the path is not absolute). The absolute path cannot be DOS-style,
- # because $ac_f cannot contain `:'.
- test -f "$ac_f" ||
- case $ac_f in
- [\\/$]*) false;;
- *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
- esac ||
- as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;;
- esac
- case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
- as_fn_append ac_file_inputs " '$ac_f'"
- done
-
- # Let's still pretend it is `configure' which instantiates (i.e., don't
- # use $as_me), people would be surprised to read:
- # /* config.h. Generated by config.status. */
- configure_input='Generated from '`
- $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
- `' by configure.'
- if test x"$ac_file" != x-; then
- configure_input="$ac_file. $configure_input"
- { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
-$as_echo "$as_me: creating $ac_file" >&6;}
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "$CONFIG_FILES"; then
+ # Protect against being on the right side of a sed subst in config.status.
+ sed 's/,@/@@/; s/@,/@@/; s/,;t t$/@;t t/; /@;t t$/s/[\\&,]/\\&/g;
+ s/@@/,@/; s/@@/@,/; s/@;t t$/,;t t/' >$tmp/subs.sed <<\CEOF
+s,@SHELL@,/bin/sh,;t t
+s,@PATH_SEPARATOR@,:,;t t
+s,@PACKAGE_NAME@,RT,;t t
+s,@PACKAGE_TARNAME@,rt,;t t
+s,@PACKAGE_VERSION@,3.6.4,;t t
+s,@PACKAGE_STRING@,RT 3.6.4,;t t
+s,@PACKAGE_BUGREPORT@,rt-bugs@bestpractical.com,;t t
+s,@exec_prefix@,/opt/rt3,;t t
+s,@prefix@,/opt/rt3,;t t
+s,@program_transform_name@,s,x,x,,;t t
+s,@bindir@,/opt/rt3/bin,;t t
+s,@sbindir@,/opt/rt3/sbin,;t t
+s,@libexecdir@,${exec_prefix}/libexec,;t t
+s,@datadir@,/opt/rt3/share,;t t
+s,@sysconfdir@,/opt/rt3/etc,;t t
+s,@sharedstatedir@,${prefix}/com,;t t
+s,@localstatedir@,/opt/rt3/var,;t t
+s,@libdir@,/opt/rt3/lib,;t t
+s,@includedir@,${prefix}/include,;t t
+s,@oldincludedir@,/usr/include,;t t
+s,@infodir@,${prefix}/info,;t t
+s,@mandir@,/opt/rt3/man,;t t
+s,@build_alias@,,;t t
+s,@host_alias@,,;t t
+s,@target_alias@,,;t t
+s,@DEFS@,-DPACKAGE_NAME=\"RT\" -DPACKAGE_TARNAME=\"rt\" -DPACKAGE_VERSION=\"3.6.4\" -DPACKAGE_STRING=\"RT\ 3.6.4\" -DPACKAGE_BUGREPORT=\"rt-bugs@bestpractical.com\" ,;t t
+s,@ECHO_C@,,;t t
+s,@ECHO_N@,-n,;t t
+s,@ECHO_T@,,;t t
+s,@LIBS@,,;t t
+s,@rt_version_major@,3,;t t
+s,@rt_version_minor@,6,;t t
+s,@rt_version_patch@,4,;t t
+s,@INSTALL_PROGRAM@,${INSTALL},;t t
+s,@INSTALL_SCRIPT@,${INSTALL},;t t
+s,@INSTALL_DATA@,${INSTALL} -m 644,;t t
+s,@AWK@,gawk,;t t
+s,@PERL@,/usr/bin/perl,;t t
+s,@SPEEDY_BIN@,/usr/local/bin/speedy,;t t
+s,@exp_prefix@,/opt/rt3,;t t
+s,@exp_exec_prefix@,/opt/rt3,;t t
+s,@exp_bindir@,/opt/rt3/bin,;t t
+s,@exp_sbindir@,/opt/rt3/sbin,;t t
+s,@exp_sysconfdir@,/opt/rt3/etc,;t t
+s,@exp_mandir@,/opt/rt3/man,;t t
+s,@exp_libdir@,/opt/rt3/lib,;t t
+s,@exp_datadir@,/opt/rt3/share,;t t
+s,@htmldir@,/var/www/freeside/rt,;t t
+s,@exp_htmldir@,/var/www/freeside/rt,;t t
+s,@manualdir@,/opt/rt3/share/doc,;t t
+s,@exp_manualdir@,/opt/rt3/share/doc,;t t
+s,@exp_localstatedir@,/opt/rt3/var,;t t
+s,@logfiledir@,/opt/rt3/var/log,;t t
+s,@exp_logfiledir@,/opt/rt3/var/log,;t t
+s,@masonstatedir@,/usr/local/etc/freeside/masondata,;t t
+s,@exp_masonstatedir@,/usr/local/etc/freeside/masondata,;t t
+s,@sessionstatedir@,/opt/rt3/var/session_data,;t t
+s,@exp_sessionstatedir@,/opt/rt3/var/session_data,;t t
+s,@customdir@,/opt/rt3/local,;t t
+s,@exp_customdir@,/opt/rt3/local,;t t
+s,@custometcdir@,/opt/rt3/local/etc,;t t
+s,@exp_custometcdir@,/opt/rt3/local/etc,;t t
+s,@customhtmldir@,/opt/rt3/local/html,;t t
+s,@exp_customhtmldir@,/opt/rt3/local/html,;t t
+s,@customlexdir@,/opt/rt3/local/po,;t t
+s,@exp_customlexdir@,/opt/rt3/local/po,;t t
+s,@customlibdir@,/opt/rt3/local/lib,;t t
+s,@exp_customlibdir@,/opt/rt3/local/lib,;t t
+s,@rt_layout_name@,Freeside,;t t
+s,@BIN_OWNER@,root,;t t
+s,@LIBS_OWNER@,root,;t t
+s,@LIBS_GROUP@,bin,;t t
+s,@DB_TYPE@,Pg,;t t
+s,@DATABASE_ENV_PREF@,,;t t
+s,@DB_HOST@,localhost,;t t
+s,@DB_PORT@,,;t t
+s,@DB_RT_HOST@,localhost,;t t
+s,@DB_DBA@,freeside,;t t
+s,@DB_DATABASE@,freeside,;t t
+s,@DB_RT_USER@,freeside,;t t
+s,@DB_RT_PASS@,,;t t
+s,@WEB_USER@,freeside,;t t
+s,@WEB_GROUP@,freeside,;t t
+s,@RTGROUP@,freeside,;t t
+s,@APACHECTL@,/usr/sbin/apachectl,;t t
+s,@RT_STANDALONE@,0,;t t
+s,@RT_DEVEL_MODE@,0,;t t
+s,@RT_VERSION_MAJOR@,3,;t t
+s,@RT_VERSION_MINOR@,6,;t t
+s,@RT_VERSION_PATCH@,4,;t t
+s,@RT_PATH@,/opt/rt3,;t t
+s,@RT_DOC_PATH@,/opt/rt3/share/doc,;t t
+s,@RT_LOCAL_PATH@,/opt/rt3/local,;t t
+s,@RT_LIB_PATH@,/opt/rt3/lib,;t t
+s,@RT_ETC_PATH@,/opt/rt3/etc,;t t
+s,@CONFIG_FILE_PATH@,/opt/rt3/etc,;t t
+s,@RT_BIN_PATH@,/opt/rt3/bin,;t t
+s,@RT_SBIN_PATH@,/opt/rt3/sbin,;t t
+s,@RT_VAR_PATH@,/opt/rt3/var,;t t
+s,@RT_MAN_PATH@,/opt/rt3/man,;t t
+s,@MASON_DATA_PATH@,/usr/local/etc/freeside/masondata,;t t
+s,@MASON_SESSION_PATH@,/opt/rt3/var/session_data,;t t
+s,@MASON_HTML_PATH@,/var/www/freeside/rt,;t t
+s,@LOCAL_ETC_PATH@,/opt/rt3/local/etc,;t t
+s,@MASON_LOCAL_HTML_PATH@,/opt/rt3/local/html,;t t
+s,@LOCAL_LEXICON_PATH@,/opt/rt3/local/po,;t t
+s,@LOCAL_LIB_PATH@,/opt/rt3/local/lib,;t t
+s,@DESTDIR@,/opt/rt3,;t t
+s,@RT_LOG_PATH@,/opt/rt3/var/log,;t t
+s,@LIBOBJS@,,;t t
+s,@LTLIBOBJS@,,;t t
+CEOF
+
+ # Split the substitutions into bite-sized pieces for seds with
+ # small command number limits, like on Digital OSF/1 and HP-UX.
+ ac_max_sed_lines=48
+ ac_sed_frag=1 # Number of current file.
+ ac_beg=1 # First line for current file.
+ ac_end=$ac_max_sed_lines # Line after last line for current file.
+ ac_more_lines=:
+ ac_sed_cmds=
+ while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+ else
+ sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
fi
- # Neutralize special characters interpreted by sed in replacement strings.
- case $configure_input in #(
- *\&* | *\|* | *\\* )
- ac_sed_conf_input=`$as_echo "$configure_input" |
- sed 's/[\\\\&|]/\\\\&/g'`;; #(
- *) ac_sed_conf_input=$configure_input;;
- esac
-
- case $ac_tag in
- *:-:* | *:-) cat >"$tmp/stdin" \
- || as_fn_error "could not create $ac_file" "$LINENO" 5 ;;
- esac
- ;;
+ if test ! -s $tmp/subs.frag; then
+ ac_more_lines=false
+ else
+ # The purpose of the label and of the branching condition is to
+ # speed up the sed processing (if there are no `@' at all, there
+ # is no need to browse any of the substitutions).
+ # These are the two extra sed commands mentioned above.
+ (echo ':t
+ /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+ fi
+ ac_sed_frag=`expr $ac_sed_frag + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_lines`
+ fi
+ done
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+ fi
+fi # test -n "$CONFIG_FILES"
+
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case $ac_file in
+ - | *:- | *:-:* ) # input from stdin
+ cat >$tmp/stdin
+ ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+ ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+ * ) ac_file_in=$ac_file.in ;;
esac
- ac_dir=`$as_dirname -- "$ac_file" ||
+ # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+ ac_dir=`(dirname "$ac_file") 2>/dev/null ||
$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$ac_file" : 'X\(//\)[^/]' \| \
X"$ac_file" : 'X\(//\)$' \| \
- X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$ac_file" |
- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
- s//\1/
- q
- }
- /^X\(\/\/\)[^/].*/{
- s//\1/
- q
- }
- /^X\(\/\/\)$/{
- s//\1/
- q
- }
- /^X\(\/\).*/{
- s//\1/
- q
- }
- s/.*/./; q'`
- as_dir="$ac_dir"; as_fn_mkdir_p
+ X"$ac_file" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ { if $as_mkdir_p; then
+ mkdir -p "$ac_dir"
+ else
+ as_dir="$ac_dir"
+ as_dirs=
+ while test ! -d "$as_dir"; do
+ as_dirs="$as_dir $as_dirs"
+ as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| \
+ . : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+ /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+ /^X\(\/\/\)$/{ s//\1/; q; }
+ /^X\(\/\).*/{ s//\1/; q; }
+ s/.*/./; q'`
+ done
+ test ! -n "$as_dirs" || mkdir $as_dirs
+ fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+ { (exit 1); exit 1; }; }; }
+
ac_builddir=.
-case "$ac_dir" in
-.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
-*)
- ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
- # A ".." for each directory in $ac_dir_suffix.
- ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
- case $ac_top_builddir_sub in
- "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
- *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
- esac ;;
-esac
-ac_abs_top_builddir=$ac_pwd
-ac_abs_builddir=$ac_pwd$ac_dir_suffix
-# for backward compatibility:
-ac_top_builddir=$ac_top_build_prefix
+if test "$ac_dir" != .; then
+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+ ac_dir_suffix= ac_top_builddir=
+fi
case $srcdir in
- .) # We are building in place.
+ .) # No --srcdir option. We are building in place.
ac_srcdir=.
- ac_top_srcdir=$ac_top_builddir_sub
- ac_abs_top_srcdir=$ac_pwd ;;
- [\\/]* | ?:[\\/]* ) # Absolute name.
+ if test -z "$ac_top_builddir"; then
+ ac_top_srcdir=.
+ else
+ ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+ fi ;;
+ [\\/]* | ?:[\\/]* ) # Absolute path.
ac_srcdir=$srcdir$ac_dir_suffix;
- ac_top_srcdir=$srcdir
- ac_abs_top_srcdir=$srcdir ;;
- *) # Relative name.
- ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
- ac_top_srcdir=$ac_top_build_prefix$srcdir
- ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+ ac_top_srcdir=$srcdir ;;
+ *) # Relative path.
+ ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_builddir$srcdir ;;
esac
-ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+ case "$ac_dir" in
+ .) ac_abs_builddir=`pwd`;;
+ [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+ *) ac_abs_builddir=`pwd`/"$ac_dir";;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+ case ${ac_top_builddir}. in
+ .) ac_abs_top_builddir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+ *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+ case $ac_srcdir in
+ .) ac_abs_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+ *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+ esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+ case $ac_top_srcdir in
+ .) ac_abs_top_srcdir=$ac_abs_builddir;;
+ [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+ *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+ esac;;
+esac
- case $ac_mode in
- :F)
- #
- # CONFIG_FILE
- #
case $INSTALL in
[\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
- *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
esac
-# If the template does not know about datarootdir, expand it.
-# FIXME: This hack should be removed a few years after 2.60.
-ac_datarootdir_hack=; ac_datarootdir_seen=
-ac_sed_dataroot='
-/datarootdir/ {
- p
- q
-}
-/@datadir@/p
-/@docdir@/p
-/@infodir@/p
-/@localedir@/p
-/@mandir@/p'
-case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
-*datarootdir*) ac_datarootdir_seen=yes;;
-*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
-$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
- ac_datarootdir_hack='
- s&@datadir@&share&g
- s&@docdir@&${datarootdir}/doc/${PACKAGE_TARNAME}&g
- s&@infodir@&${datarootdir}/info&g
- s&@localedir@&${datarootdir}/locale&g
- s&@mandir@&man&g
- s&\${datarootdir}&${prefix}/share&g' ;;
-esac
-ac_sed_extra="/^[ ]*VPATH[ ]*=/{
-s/:*\$(srcdir):*/:/
-s/:*\${srcdir}:*/:/
-s/:*@srcdir@:*/:/
-s/^\([^=]*=[ ]*\):*/\1/
-s/:*$//
-s/^[^=]*=[ ]*$//
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ if test x"$ac_file" = x-; then
+ configure_input=
+ else
+ configure_input="$ac_file. "
+ fi
+ configure_input=$configure_input"Generated from `echo $ac_file_in |
+ sed 's,.*/,,'` by configure."
+
+ # First look for the input files in the build tree, otherwise in the
+ # src tree.
+ ac_file_inputs=`IFS=:
+ for f in $ac_file_in; do
+ case $f in
+ -) echo $tmp/stdin ;;
+ [\\/$]*)
+ # Absolute (can't be DOS-style, as IFS=:)
+ test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ echo "$f";;
+ *) # Relative
+ if test -f "$f"; then
+ # Build tree
+ echo "$f"
+ elif test -f "$srcdir/$f"; then
+ # Source tree
+ echo "$srcdir/$f"
+ else
+ # /dev/null tree
+ { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+ { (exit 1); exit 1; }; }
+ fi;;
+ esac
+ done` || { (exit 1); exit 1; }
+
+ if test x"$ac_file" != x-; then
+ { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+ rm -f "$ac_file"
+ fi
+ sed "/^[ ]*VPATH[ ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[ ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[ ]*$//;
}
:t
/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
-s|@configure_input@|$ac_sed_conf_input|;t t
-s&@top_builddir@&$ac_top_builddir_sub&;t t
-s&@top_build_prefix@&$ac_top_build_prefix&;t t
-s&@srcdir@&$ac_srcdir&;t t
-s&@abs_srcdir@&$ac_abs_srcdir&;t t
-s&@top_srcdir@&$ac_top_srcdir&;t t
-s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
-s&@builddir@&$ac_builddir&;t t
-s&@abs_builddir@&$ac_abs_builddir&;t t
-s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
-s&@INSTALL@&$ac_INSTALL&;t t
-$ac_datarootdir_hack
-"
-eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
- || as_fn_error "could not create $ac_file" "$LINENO" 5
-
-test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
- { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
- { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
-which seems to be undefined. Please make sure it is defined." >&5
-$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
-which seems to be undefined. Please make sure it is defined." >&2;}
-
- rm -f "$tmp/stdin"
- case $ac_file in
- -) cat "$tmp/out" && rm -f "$tmp/out";;
- *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
- esac \
- || as_fn_error "could not create $ac_file" "$LINENO" 5
- ;;
-
-
-
- esac
-
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+ rm -f $tmp/stdin
+ if test x"$ac_file" != x-; then
+ mv $tmp/out $ac_file
+ else
+ cat $tmp/out
+ rm -f $tmp/out
+ fi
- case $ac_file$ac_mode in
- "etc/upgrade/3.8-branded-queues-extension":F) chmod ug+x $ac_file
- ;;
- "etc/upgrade/3.8-ical-extension":F) chmod ug+x $ac_file
- ;;
- "etc/upgrade/split-out-cf-categories":F) chmod ug+x $ac_file
- ;;
- "sbin/rt-attributes-viewer":F) chmod ug+x $ac_file
- ;;
- "sbin/rt-dump-database":F) chmod ug+x $ac_file
- ;;
- "sbin/rt-setup-database":F) chmod ug+x $ac_file
- ;;
- "sbin/rt-test-dependencies":F) chmod ug+x $ac_file
- ;;
- "sbin/rt-email-digest":F) chmod ug+x $ac_file
- ;;
- "sbin/rt-email-dashboards":F) chmod ug+x $ac_file
- ;;
- "sbin/rt-clean-sessions":F) chmod ug+x $ac_file
- ;;
- "sbin/rt-shredder":F) chmod ug+x $ac_file
- ;;
- "sbin/rt-validator":F) chmod ug+x $ac_file
+ # Run the commands associated with the file.
+ case $ac_file in
+ sbin/rt-dump-database ) chmod ug+x $ac_file
;;
- "sbin/rt-email-group-admin":F) chmod ug+x $ac_file
+ sbin/rt-setup-database ) chmod ug+x $ac_file
;;
- "sbin/rt-server":F) chmod ug+x $ac_file
+ sbin/rt-test-dependencies ) chmod ug+x $ac_file
;;
- "bin/mason_handler.fcgi":F) chmod ug+x $ac_file
+ bin/mason_handler.fcgi ) chmod ug+x $ac_file
;;
- "bin/mason_handler.scgi":F) chmod ug+x $ac_file
+ bin/mason_handler.scgi ) chmod ug+x $ac_file
;;
- "bin/standalone_httpd":F) chmod ug+x $ac_file
+ bin/standalone_httpd ) chmod ug+x $ac_file
;;
- "bin/rt-crontool":F) chmod ug+x $ac_file
+ bin/rt-crontool ) chmod ug+x $ac_file
;;
- "bin/rt-mailgate":F) chmod ug+x $ac_file
+ bin/rt-mailgate ) chmod ug+x $ac_file
;;
- "bin/rt":F) chmod ug+x $ac_file
+ bin/rt ) chmod ug+x $ac_file
;;
-
esac
-done # for ac_tag
-
+done
-as_fn_exit 0
+{ (exit 0); exit 0; }
diff --git a/rt/etc/RT_Config.pm b/rt/etc/RT_Config.pm
index 823189b45..7f7eadcca 100644
--- a/rt/etc/RT_Config.pm
+++ b/rt/etc/RT_Config.pm
@@ -1,3 +1,7 @@
+#
+# WARNING: NEVER EDIT RT_Config.pm. Instead, copy any sections you want to change to RT_SiteConfig.pm
+# and edit them there.
+#
package RT;
@@ -11,1220 +15,472 @@ use RT::Config;
=cut
-=head1 WARNING
+# {{{ Base Configuration
-NEVER EDIT RT_Config.pm.
+# $rtname is the string that RT will look for in mail messages to
+# figure out what ticket a new piece of mail belongs to
-Instead, copy any sections you want to change to F<RT_SiteConfig.pm> and edit them there.
-
-=cut
-
-=head1 Base Configuration
-
-=over 4
-
-=item C<$rtname>
-
-C<$rtname> is the string that RT will look for in mail messages to
-figure out what ticket a new piece of mail belongs to.
-
-Your domain name is recommended, so as not to pollute the namespace.
-once you start using a given tag, you should probably never change it.
-(otherwise, mail for existing tickets won't get put in the right place)
-
-=cut
+# Your domain name is recommended, so as not to pollute the namespace.
+# once you start using a given tag, you should probably never change it.
+# (otherwise, mail for existing tickets won't get put in the right place
Set($rtname , "example.com");
-=item C<$EmailSubjectTagRegex>
-
-This regexp controls what subject tags RT recognizes as its own.
-If you're not dealing with historical C<$rtname> values, you'll likely
-never have to enable this feature.
+# This regexp controls what subject tags RT recognizes as its own.
+# If you're not dealing with historical $rtname values, you'll likely
+# never have to enable this feature.
+#
+# Be VERY CAREFUL with it. Note that it overrides $rtname for subject
+# token matching and that you should use only "non-capturing" parenthesis
+# grouping. For example:
+#
+# Set($EmailSubjectTagRegex, qr/(?:example.com|example.org)/i );
+#
+# and NOT
+#
+# Set($EmailSubjectTagRegex, qr/(example.com|example.org)/i );
+#
+# This setting would make RT behave exactly as it does without the
+# setting enabled.
+#
+# Set($EmailSubjectTagRegex, qr/\Q$rtname\E/i );
-Be VERY CAREFUL with it. Note that it overrides C<$rtname> for subject
-token matching and that you should use only "non-capturing" parenthesis
-grouping. For example:
-C<Set($EmailSubjectTagRegex, qr/(?:example.com|example.org)/i );>
-and NOT
-
-C<Set($EmailSubjectTagRegex, qr/(example.com|example.org)/i );>
-
-This setting would make RT behave exactly as it does without the
-setting enabled.
-
-=cut
-
-#Set($EmailSubjectTagRegex, qr/\Q$rtname\E/i );
-
-
-
-=item C<$Organization>
-
-You should set this to your organization's DNS domain. For example,
-I<fsck.com> or I<asylum.arkham.ma.us>. It's used by the linking interface to
-guarantee that ticket URIs are unique and easy to construct.
-
-=cut
+# You should set this to your organization's DNS domain. For example,
+# fsck.com or asylum.arkham.ma.us. It's used by the linking interface to
+# guarantee that ticket URIs are unique and easy to construct.
Set($Organization , "example.com");
-=item C<$MinimumPasswordLength>
-
-C<$MinimumPasswordLength> defines the minimum length for user
-passwords. Setting it to 0 disables this check.
-
-=cut
-
+# $user_passwd_min defines the minimum length for user passwords. Setting
+# it to 0 disables this check
Set($MinimumPasswordLength , "5");
-=item C<$Timezone>
-
-C<$Timezone> is used to convert times entered by users into GMT and back again
-It should be set to a timezone recognized by your local unix box.
-
-=cut
-
+# $Timezone is used to convert times entered by users into GMT and back again
+# It should be set to a timezone recognized by your local unix box.
Set($Timezone , 'US/Eastern');
-=back
-
-=head1 Database Configuration
+# }}}
-=over 4
+# {{{ Database Configuration
-=item C<$DatabaseType>
+# Database driver beeing used. Case matters
+# Valid types are "mysql", "Oracle" and "Pg"
-Database driver being used; case matters.
-
-Valid types are "mysql", "Oracle" and "Pg"
-
-=cut
-
-Set($DatabaseType , 'SQLite');
-
-=item C<$DatabaseHost>, C<$DatabaseRTHost>
-
-The domain name of your database server.
-
-If you're running mysql and it's on localhost,
-leave it blank for enhanced performance
-
-=cut
+Set($DatabaseType , 'Pg');
+# The domain name of your database server
+# If you're running mysql and it's on localhost,
+# leave it blank for enhanced performance
Set($DatabaseHost , 'localhost');
Set($DatabaseRTHost , 'localhost');
-=item C<$DatabasePort>
-
-The port that your database server is running on. Ignored unless it's
-a positive integer. It's usually safe to leave this blank
-
-=cut
-
+# The port that your database server is running on. Ignored unless it's
+# a positive integer. It's usually safe to leave this blank
Set($DatabasePort , '');
-=item C<$DatabaseUser>
-
-The name of the database user (inside the database)
-
-=cut
-
-Set($DatabaseUser , 'rt_user');
-
-=item C<$DatabasePassword>
-
-Password the C<$DatabaseUser> should use to access the database
-
-=cut
-
-Set($DatabasePassword , 'rt_pass');
-
-=item C<$DatabaseName>
-
-The name of the RT's database on your database server. For Oracle
-it's SID, DB objects are created in L<$DatabaseUser>'s schema.
-
-=cut
-
-Set($DatabaseName , 'rt3');
-
-=item C<$DatabaseRequireSSL>
+#The name of the database user (inside the database)
+Set($DatabaseUser , 'freeside');
-If you're using Postgres and have compiled in SSL support,
-set C<$DatabaseRequireSSL> to 1 to turn on SSL communication
+# Password the DatabaseUser should use to access the database
+Set($DatabasePassword , '');
-=cut
+# The name of the RT's database on your database server
+Set($DatabaseName , 'freeside');
+# If you're using Postgres and have compiled in SSL support,
+# set DatabaseRequireSSL to 1 to turn on SSL communication
Set($DatabaseRequireSSL , undef);
-=item C<$UseSQLForACLChecks>
-
-In RT for ages ACL are checked after search what in some situtations
-result in empty search pages and wrong count of tickets.
-
-Set C<$UseSQLForACLChecks> to 1 to use SQL and get rid of these problems.
-
-However, this option is beta. In some cases it result in performance
-improvements, but some setups can not handle it.
-
-=cut
-
-Set($UseSQLForACLChecks, undef);
-
-=back
-
-=head1 Incoming Mail Gateway Configuration
-
-=over 4
+# }}}
-=item C<$OwnerEmail>
+# {{{ Incoming mail gateway configuration
-C<$OwnerEmail> is the address of a human who manages RT. RT will send
-errors generated by the mail gateway to this address. This address
-should _not_ be an address that's managed by your RT instance.
-
-=cut
+# OwnerEmail is the address of a human who manages RT. RT will send
+# errors generated by the mail gateway to this address. This address
+# should _not_ be an address that's managed by your RT instance.
Set($OwnerEmail , 'root');
-=item C<$LoopsToRTOwner>
-
-If C<$LoopsToRTOwner> is defined, RT will send mail that it believes
-might be a loop to C<$OwnerEmail>
-
-=cut
+# If $LoopsToRTOwner is defined, RT will send mail that it believes
+# might be a loop to $RT::OwnerEmail
Set($LoopsToRTOwner , 1);
-=item C<$StoreLoops>
-
-If C<$StoreLoops> is defined, RT will record messages that it believes
-to be part of mail loops.
-
-As it does this, it will try to be careful not to send mail to the
-sender of these messages
-
-=cut
+# If $StoreLoops is defined, RT will record messages that it believes
+# to be part of mail loops.
+# As it does this, it will try to be careful not to send mail to the
+# sender of these messages
Set($StoreLoops , undef);
-=item C<$MaxAttachmentSize>
-
-C<$MaxAttachmentSize> sets the maximum size (in bytes) of attachments stored
-in the database.
-
-For mysql and oracle, we set this size at 10 megabytes.
-If you're running a postgres version earlier than 7.1, you will need
-to drop this to 8192. (8k)
-
-=cut
+# $MaxAttachmentSize sets the maximum size (in bytes) of attachments stored
+# in the database.
+# For mysql and oracle, we set this size at 10 megabytes.
+# If you're running a postgres version earlier than 7.1, you will need
+# to drop this to 8192. (8k)
Set($MaxAttachmentSize , 10000000);
-=item C<$TruncateLongAttachments>
-
-C<$TruncateLongAttachments>: if this is set to a non-undef value,
-RT will truncate attachments longer than C<$MaxAttachmentSize>.
-
-=cut
+# $TruncateLongAttachments: if this is set to a non-undef value,
+# RT will truncate attachments longer than MaxAttachmentSize.
Set($TruncateLongAttachments , undef);
-=item C<$DropLongAttachments>
-
-C<$DropLongAttachments>: if this is set to a non-undef value,
-RT will silently drop attachments longer than C<MaxAttachmentSize>.
-
-=cut
+# $DropLongAttachments: if this is set to a non-undef value,
+# RT will silently drop attachments longer than MaxAttachmentSize.
Set($DropLongAttachments , undef);
-=item C<$ParseNewMessageForTicketCcs>
-
-If C<$ParseNewMessageForTicketCcs> is true, RT will attempt to divine
-Ticket 'Cc' watchers from the To and Cc lines of incoming messages
-Be forewarned that if you have _any_ addresses which forward mail to
-RT automatically and you enable this option without modifying
-C<$RTAddressRegexp> below, you will get yourself into a heap of trouble.
-
-=cut
+# If $ParseNewMessageForTicketCcs is true, RT will attempt to divine
+# Ticket 'Cc' watchers from the To and Cc lines of incoming messages
+# Be forewarned that if you have _any_ addresses which forward mail to
+# RT automatically and you enable this option without modifying
+# "RTAddressRegexp" below, you will get yourself into a heap of trouble.
Set($ParseNewMessageForTicketCcs , undef);
-=item C<$RTAddressRegexp>
-
-C<$RTAddressRegexp> is used to make sure RT doesn't add itself as a ticket CC if
-the setting above is enabled. It is important that you set this to a
-regular expression that matches all addresses used by your RT. This lets RT
-avoid sending mail to itself. It will also hide RT addresses from the list of
-"One-time Cc" and Bcc lists on ticket reply.
-
-=cut
+# RTAddressRegexp is used to make sure RT doesn't add itself as a ticket CC if
+# the setting above is enabled.
Set($RTAddressRegexp , '^rt\@example.com$');
-=item C<$CanonicalizeEmailAddressMatch>, C<$CanonicalizeEmailAddressReplace>
-
-RT provides functionality which allows the system to rewrite
-incoming email addresses. In its simplest form,
-you can substitute the value in $<CanonicalizeEmailAddressReplace>
-for the value in $<CanonicalizeEmailAddressMatch>
-(These values are passed to the $<CanonicalizeEmailAddress> subroutine in
- F<RT/User.pm>)
-
-By default, that routine performs a C<s/$Match/$Replace/gi> on any address
-passed to it.
-
-=cut
+# RT provides functionality which allows the system to rewrite
+# incoming email addresses. In its simplest form,
+# you can substitute the value in CanonicalizeEmailAddressReplace
+# for the value in CanonicalizeEmailAddressMatch
+# (These values are passed to the CanonicalizeEmailAddress subroutine in RT/User.pm)
+# By default, that routine performs a s/$Match/$Replace/gi on any address passed to it
#Set($CanonicalizeEmailAddressMatch , '@subdomain\.example\.com$');
#Set($CanonicalizeEmailAddressReplace , '@example.com');
-=item C<$CanonicalizeEmailAddressMatch>
-
-Set this to true and the create new user page will use the values that you
-enter in the form but use the function CanonicalizeUserInfo in
-F<RT/User_Local.pm>
-
-=cut
-
-Set($CanonicalizeOnCreate, 0);
-
-=item C<$SenderMustExistInExternalDatabase>
-
-If C<$SenderMustExistInExternalDatabase> is true, RT will refuse to
-create non-privileged accounts for unknown users if you are using
-the C<$LookupSenderInExternalDatabase> option.
-Instead, an error message will be mailed and RT will forward the
-message to C<$RTOwner>.
-
-If you are not using C<$LookupSenderInExternalDatabase>, this option
-has no effect.
-
-If you define an AutoRejectRequest template, RT will use this
-template for the rejection message.
-
-=cut
+# set this to true and the create new user page will use the values that you
+# enter in the form but use the function CanonicalizeUserInfo in User_Local.pm
+Set($CanonicalizeOnCreate , 0);
+
+# If $SenderMustExistInExternalDatabase is true, RT will refuse to
+# create non-privileged accounts for unknown users if you are using
+# the "LookupSenderInExternalDatabase" option.
+# Instead, an error message will be mailed and RT will forward the
+# message to $RTOwner.
+#
+# If you are not using $LookupSenderInExternalDatabase, this option
+# has no effect.
+#
+# If you define an AutoRejectRequest template, RT will use this
+# template for the rejection message.
Set($SenderMustExistInExternalDatabase , undef);
-=item C<$ValidateUserEmailAddresses>
-
-If C<$ValidateUserEmailAddresses> is true, RT will refuse to create users with
-an invalid email address (as specified in RFC 2822) or with an email address
-made of multiple email adresses.
-
-=cut
-
-Set($ValidateUserEmailAddresses, undef);
-
-=item C<@MailPlugins>
-
-C<@MailPlugins> is a list of auth plugins for L<RT::Interface::Email>
-to use; see L<rt-mailgate>
-
-=cut
-
-=item C<$UnsafeEmailCommands>
-
-C<$UnsafeEmailCommands>, if set to true, enables 'take' and 'resolve'
-as possible actions via the mail gateway. As its name implies, this
-is very unsafe, as it allows email with a forged sender to possibly
-resolve arbitrary tickets!
-
-=cut
-
-=item C<$ExtractSubjectTagMatch>, C<$ExtractSubjectTagNoMatch>
+# }}}
-The default "extract remote tracking tags" scrip settings; these
-detect when your RT is talking to another RT, and adjusts the
-subject accordingly.
+# {{{ Outgoing mail configuration
-=cut
-
-Set($ExtractSubjectTagMatch, qr/\[.+? #\d+\]/);
-Set($ExtractSubjectTagNoMatch, ( ${RT::EmailSubjectTagRegex}
- ? qr/\[(?:${RT::EmailSubjectTagRegex}) #\d+\]/
- : qr/\[\Q$RT::rtname\E #\d+\]/));
-
-=back
+# RT is designed such that any mail which already has a ticket-id associated
+# with it will get to the right place automatically.
-=head1 Outgoing Mail Configuration
+# $CorrespondAddress and $CommentAddress are the default addresses
+# that will be listed in From: and Reply-To: headers of correspondence
+# and comment mail tracked by RT, unless overridden by a queue-specific
+# address.
-=over 4
+Set($CorrespondAddress , 'RT_CorrespondAddressNotSet');
-=item C<$MailCommand>
+Set($CommentAddress , 'RT_CommentAddressNotSet');
-C<$MailCommand> defines which method RT will use to try to send mail.
-We know that 'sendmailpipe' works fairly well. If 'sendmailpipe'
-doesn't work well for you, try 'sendmail'. Other options are 'smtp'
-or 'qmail'.
+#Sendmail Configuration
-Note that you should remove the '-t' from C<$SendmailArguments>
-if you use 'sendmail' rather than 'sendmailpipe'
-
-=cut
+# $MailCommand defines which method RT will use to try to send mail
+# We know that 'sendmailpipe' works fairly well.
+# If 'sendmailpipe' doesn't work well for you, try 'sendmail'
+#
+# Note that you should remove the '-t' from $SendmailArguments
+# if you use 'sendmail' rather than 'sendmailpipe'
Set($MailCommand , 'sendmailpipe');
-=item C<$SetOutgoingMailFrom>
-
-C<$SetOutgoingMailFrom> tells RT to set the sender envelope with the correspond
-mail address of the ticket's queue.
-
-Warning: If you use this setting, bounced mails will appear to be incoming
-mail to the system, thus creating new tickets.
-
-=cut
-
-Set($SetOutgoingMailFrom, 0);
-
-=item C<$OverrideOutgoingMailFrom>
-
-C<$OverrideOutgoingMailFrom> is used for overwriting the Correspond
-address of the queue. The option is a hash reference of queue name to
-email address.
-
-If there is no ticket involved, then the value of the C<Default> key will be
-used.
-
-=cut
-
-Set($OverrideOutgoingMailFrom, {
-# 'Default' => 'admin@rt.example.com',
-# 'General' => 'general@rt.example.com',
-});
-
-=back
-
-=head1 Sendmail Configuration
-
-These options only take effect if C<$MailCommand> is 'sendmail' or
-'sendmailpipe'
-
-=over 4
-
-=item C<$SendmailArguments>
-
-C<$SendmailArguments> defines what flags to pass to C<$SendmailPath>
-If you picked 'sendmailpipe', you MUST add a -t flag to C<$SendmailArguments>
-These options are good for most sendmail wrappers and workalikes
-
-These arguments are good for sendmail brand sendmail 8 and newer
-C<Set($SendmailArguments,"-oi -t -ODeliveryMode=b -OErrorMode=m");>
-
-=cut
+# $SendmailArguments defines what flags to pass to $Sendmail
+# assuming you picked 'sendmail' or 'sendmailpipe' as the $MailCommand above.
+# If you picked 'sendmailpipe', you MUST add a -t flag to $SendmailArguments
+# These options are good for most sendmail wrappers and workalikes
Set($SendmailArguments , "-oi -t");
-
-=item C<$SendmailBounceArguments>
-
-C<$SendmailBounceArguments> defines what flags to pass to C<$Sendmail>
-assuming RT needs to send an error (ie. bounce).
-
-=cut
+# $SendmailBounceArguments defines what flags to pass to $Sendmail
+# assuming RT needs to send an error (ie. bounce).
Set($SendmailBounceArguments , '-f "<>"');
-=item C<$SendmailPath>
-
-If you selected 'sendmailpipe' above, you MUST specify the path to
-your sendmail binary in C<$SendmailPath>.
-
-=cut
+# These arguments are good for sendmail brand sendmail 8 and newer
+#Set($SendmailArguments,"-oi -t -ODeliveryMode=b -OErrorMode=m");
+# If you selected 'sendmailpipe' above, you MUST specify the path
+# to your sendmail binary in $SendmailPath.
+# !! If you did not # select 'sendmailpipe' above, this has no effect!!
Set($SendmailPath , "/usr/sbin/sendmail");
+# By default, RT sets the outgoing mail's "From:" header to
+# "SenderName via RT". Setting this option to 0 disables it.
-=back
+Set($UseFriendlyFromLine , 1);
-=head1 SMTP Configuration
+# sprintf() format of the friendly 'From:' header; its arguments
+# are SenderName and SenderEmailAddress.
+Set($FriendlyFromLineFormat , "\"%s via RT\" <%s>");
-These options only take effect if C<$MailCommand> is 'smtp'
+# RT can optionally set a "Friendly" 'To:' header when sending messages to
+# Ccs or AdminCcs (rather than having a blank 'To:' header.
-=over 4
+# This feature DOES NOT WORK WITH SENDMAIL[tm] BRAND SENDMAIL
+# If you are using sendmail, rather than postfix, qmail, exim or some other MTA,
+# you _must_ disable this option.
-=item C<$SMTPServer>
+Set($UseFriendlyToLine , 0);
-C<$SMTPServer> should be set to the hostname of the SMTP server to use
+# sprintf() format of the friendly 'From:' header; its arguments
+# are WatcherType and TicketId.
+Set($FriendlyToLineFormat, "\"%s of $RT::rtname Ticket #%s\":;");
-=cut
-
-Set($SMTPServer, undef);
-
-=item C<$SMTPFrom>
-
-C<$SMTPFrom> should be set to the 'From' address to use, if not the
-email's 'From'
-
-=cut
-
-Set($SMTPFrom, undef);
-
-=item C<$SMTPDebug>
-
-C<$SMTPDebug> should be set to true to debug SMTP mail sending
-
-=cut
-
-Set($SMTPDebug, 0);
-
-=back
-
-=head1 Other Mailer Configuration
-
-=over 4
-
-=item C<@MailParams>
-
-C<@MailParams> defines a list of options passed to $MailCommand if it
-is not 'sendmailpipe', 'sendmail', or 'smtp'
-
-=cut
-
-Set(@MailParams, ());
-
-=item C<$CorrespondAddress>, C<$CommentAddress>
-
-RT is designed such that any mail which already has a ticket-id associated
-with it will get to the right place automatically.
-
-C<$CorrespondAddress> and C<$CommentAddress> are the default addresses
-that will be listed in From: and Reply-To: headers of correspondence
-and comment mail tracked by RT, unless overridden by a queue-specific
-address.
-
-=cut
-
-Set($CorrespondAddress , '');
-
-Set($CommentAddress , '');
-
-=item C<$DashboardAddress>
-
-The email address from which RT will send dashboards. If none is set, then
-C<$OwnerEmail> will be used.
-
-=cut
-
-Set($DashboardAddress, '');
-
-=item C<$UseFriendlyFromLine>
-
-By default, RT sets the outgoing mail's "From:" header to
-"SenderName via RT". Setting C<$UseFriendlyFromLine> to 0 disables it.
-
-=cut
-
-Set($UseFriendlyFromLine, 1);
-
-=item C<$FriendlyFromLineFormat>
-
-C<sprintf()> format of the friendly 'From:' header; its arguments
-are SenderName and SenderEmailAddress.
-
-=cut
-
-Set($FriendlyFromLineFormat, "\"%s via RT\" <%s>");
-
-=item C<$UseFriendlyToLine>
-
-RT can optionally set a "Friendly" 'To:' header when sending messages to
-Ccs or AdminCcs (rather than having a blank 'To:' header.
-
-This feature DOES NOT WORK WITH SENDMAIL[tm] BRAND SENDMAIL
-If you are using sendmail, rather than postfix, qmail, exim or some other MTA,
-you _must_ disable this option.
-
-=cut
-
-Set($UseFriendlyToLine, 0);
-
-=item C<$FriendlyToLineFormat>
-
-C<sprintf()> format of the friendly 'From:' header; its arguments
-are WatcherType and TicketId.
-
-=cut
-
-Set($FriendlyToLineFormat, "\"%s of ". RT->Config->Get('rtname') ." Ticket #%s\":;");
-
-=item C<$NotifyActor>
-
-By default, RT doesn't notify the person who performs an update, as they
-already know what they've done. If you'd like to change this behaviour,
-Set C<$NotifyActor> to 1
-
-=cut
+# By default, RT doesn't notify the person who performs an update, as they
+# already know what they've done. If you'd like to change this behaviour,
+# Set $NotifyActor to 1
Set($NotifyActor, 0);
-=item C<$RecordOutgoingEmail>
-
-By default, RT records each message it sends out to its own internal database.
-To change this behavior, set C<$RecordOutgoingEmail> to 0
-
-=cut
+# By default, RT records each message it sends out to its own internal database.# To change this behaviour, set $RecordOutgoingEmail to 0
Set($RecordOutgoingEmail, 1);
-=item C<$VERPPrefix>, C<$VERPPrefix>
-
-VERP support (http://cr.yp.to/proto/verp.txt)
-
-uncomment the following two directives to generate envelope senders
-of the form C<${VERPPrefix}${originaladdress}@${VERPDomain}>
-(i.e. rt-jesse=fsck.com@rt.example.com ).
-
-This currently only works with sendmail and sendmailppie.
-
-=cut
-
+# VERP support (http://cr.yp.to/proto/verp.txt)
+# uncomment the following two directives to generate envelope senders
+# of the form ${VERPPrefix}${originaladdress}@${VERPDomain}
+# (i.e. rt-jesse=fsck.com@rt.example.com ) This currently only works
+# with sendmail and sendmailppie.
# Set($VERPPrefix, 'rt-');
# Set($VERPDomain, $RT::Organization);
+# }}}
-=item C<$ForwardFromUser>
-
-By default, RT forwards a message using queue's address and adds RT's tag into
-subject of the outgoing message, so recipients' replies go into RT as correspondents.
-
-To change this behavior, set C<$ForwardFromUser> to true value and RT will use
-address of the current user and leave subject without RT's tag.
-
-=cut
-
-Set($ForwardFromUser, 0);
-
-=item C<$ShowBccHeader>
-
-By default RT hides from the web UI information about blind copies user sent on
-reply or comment.
-
-To change this set the following option to true value.
-
-=cut
-
-Set($ShowBccHeader, 0);
-
-=item C<$DashboardSubject>
-
-Lets you set the subject of dashboards. Arguments are the frequency (Daily,
-Weekly, Monthly) of the dashboard and the dashboard's name. [_1] for the name
-of the dashboard.
-
-=cut
-
-Set($DashboardSubject, '%s Dashboard: %s');
-
-=back
-
-=head1 GnuPG Configuration
-
-A full description of the (somewhat extensive) GnuPG integration can be found
-by running the command `perldoc L<RT::Crypt::GnuPG>` (or `perldoc
- lib/RT/Crypt/GnuPG.pm` from your RT install directory).
-
-=over 4
-
-=item C<%GnuPG>
-
-Set C<OutgoingMessagesFormat> to 'inline' to use inline encryption and
-signatures instead of 'RFC' (GPG/MIME: RFC3156 and RFC1847) format.
-
-If you want to allow people to encrypt attachments inside the DB then
-set C<AllowEncryptDataInDB> to true
-
-Set C<RejectOnMissingPrivateKey> to false if you don't want to reject
-emails encrypted for key RT doesn't have and can not decrypt.
-
-Set C<RejectOnBadData> to false if you don't want to reject letters
-with incorrect GnuPG data.
-
-=cut
-
-Set( %GnuPG,
- Enable => 1,
- OutgoingMessagesFormat => 'RFC', # Inline
- AllowEncryptDataInDB => 0,
-
- RejectOnMissingPrivateKey => 1,
- RejectOnBadData => 1,
-);
-
-=item C<%GnuPGOptions>
-
-Options of GnuPG program.
-
-If you override this in your RT_SiteConfig, you should be sure
-to include a homedir setting.
-
-NOTE that options with '-' character MUST be quoted.
-
-=cut
-
-Set(%GnuPGOptions,
- homedir => 'var/data/gpg',
-
-# URL of a keyserver
-# keyserver => 'hkp://subkeys.pgp.net',
-
-# enables the automatic retrieving of keys when encrypting
-# 'auto-key-locate' => 'keyserver',
-
-# enables the automatic retrieving of keys when verifying signatures
-# 'auto-key-retrieve' => undef,
-);
-
-
-=back
-
-=head1 Logging Configuration
-
-The default is to log anything except debugging
-information to syslog. Check the L<Log::Dispatch> POD for
-information about how to get things by syslog, mail or anything
-else, get debugging info in the log, etc.
-
-It might generally make sense to send error and higher by email to
-some administrator. If you do this, be careful that this email
-isn't sent to this RT instance. Mail loops will generate a critical
-log message.
-
-=over 4
-
-=item C<$LogToSyslog>, C<$LogToScreen>
-
-The minimum level error that will be logged to the specific device.
-From lowest to highest priority, the levels are:
- debug info notice warning error critical alert emergency
-
-=cut
-
-Set($LogToSyslog , 'info');
-Set($LogToScreen , 'info');
+# {{{ Logging
-=item C<$LogToFile>, C<$LogDir>, C<$LogToFileNamed>
+# Logging. The default is to log anything except debugging
+# information to syslog. Check the Log::Dispatch POD for
+# information about how to get things by syslog, mail or anything
+# else, get debugging info in the log, etc.
-Logging to a standalone file is also possible, but note that the
-file should needs to both exist and be writable by all direct users
-of the RT API. This generally include the web server, whoever
-rt-crontool runs as. Note that as rt-mailgate and the RT CLI go
-through the webserver, so their users do not need to have write
-permissions to this file. If you expect to have multiple users of
-the direct API, Best Practical recommends using syslog instead of
-direct file logging.
+# It might generally make
+# sense to send error and higher by email to some administrator.
+# If you do this, be careful that this email isn't sent to this RT instance.
-=cut
+# the minimum level error that will be logged to the specific device.
+# levels from lowest to highest:
+# debug info notice warning error critical alert emergency
+# Mail loops will generate a critical log message.
+Set($LogToSyslog , 'debug');
+Set($LogToScreen , 'error');
Set($LogToFile , undef);
-Set($LogDir, 'var/log');
+Set($LogDir, '/opt/rt3/var/log');
Set($LogToFileNamed , "rt.log"); #log to rt.log
-=item C<$LogStackTraces>
-
-If set to a log level then logging will include stack traces for
-messages with level equal to or greater than specified.
-
-NOTICE: Stack traces include parameters supplied to functions or
-methods. It is possible for stack trace logging to reveal sensitive
-information such as passwords or ticket content in your logs.
-
-=cut
-
-Set($LogStackTraces, '');
-
-=item C<@LogToSyslogConf>
+# If true generates stack traces to file log or screen
+# never generates traces to syslog
-On Solaris or UnixWare, set to ( socket => 'inet' ). Options here
-override any other options RT passes to L<Log::Dispatch::Syslog>.
-Other interesting flags include facility and logopt. (See the
-L<Log::Dispatch::Syslog> documentation for more information.) (Maybe
-ident too, if you have multiple RT installations.)
-
-=cut
+Set($LogStackTraces , 0);
-Set(@LogToSyslogConf, ());
+# On Solaris or UnixWare, set to ( socket => 'inet' ). Options here
+# override any other options RT passes to Log::Dispatch::Syslog.
+# Other interesting flags include facility and logopt. (See the
+# Log::Dispatch::Syslog documentation for more information.) (Maybe
+# ident too, if you have multiple RT installations.)
-=item C<$StatementLog>,
-
-RT has rudimentary SQL statement logging support if you have
-DBIx-SearchBuilder 1.31_1 or higher; simply set C<$StatementLog> to be
-the level that you wish SQL statements to be logged at.
-
-=cut
+@LogToSyslogConf = () unless (@LogToSyslogConf);
+# RT has rudimentary SQL statement logging support if you have
+# DBIx-SearchBuilder 1.31_1 or higher; simply set $StatementLog to be
+# the level that you wish SQL statements to be logged at.
Set($StatementLog, undef);
-=back
-
-=head1 Web Interface Configuration
-
-=over 4
-
-=item C<$WebDefaultStylesheet>
-
-This determines the default stylesheet the RT web interface will use.
-RT ships with several themes by default:
-
- web2 The totally new, default layout for RT 3.8
- 3.5-default RT 3.5 and 3.6 original layout
- 3.4-compat A 3.4 compatibility stylesheet to make RT look
- (mostly) like 3.4
-
-This value actually specifies a directory in F<share/html/NoAuth/css/>
-from which RT will try to load the file main.css (which should
-@import any other files the stylesheet needs). This allows you to
-easily and cleanly create your own stylesheets to apply to RT. This
-option can be overridden by users in their preferences.
+# }}}
-=cut
-
-Set($WebDefaultStylesheet, 'web2');
-
-=item C<$UsernameFormat>
-
-This determines how user info is displayed. 'concise' will show one of
-either NickName, RealName, Name or EmailAddress, depending on what exists
-and whether the user is privileged or not. 'verbose' will show RealName and
-EmailAddress.
-
-=cut
-
-Set($UsernameFormat, 'concise');
+# {{{ Web interface configuration
-=item C<$WebDomain>
+# This determines the default stylesheet the RT web interface will use.
+# RT ships with two valid values by default:
+#
+# 3.5-default The totally new, default layout for RT 3.5
+# 3.4-compat A 3.4 compatibility stylesheet to make RT 3.5 look
+# (mostly) like 3.4
+#
+# This value actually specifies a directory in share/html/NoAuth/css/
+# from which RT will try to load the file main.css (which should
+# @import any other files the stylesheet needs). This allows you to
+# easily and cleanly create your own stylesheets to apply to RT.
-Domain name of the RT server, eg 'www.example.com'. It should not contain
-anything else, but server name.
+Set($WebDefaultStylesheet, '3.5-default');
-=cut
+# Define the directory name to be used for images in rt web
+# documents.
-Set( $WebDomain, 'localhost' );
+# If you're putting the web ui somewhere other than at the root of
+# your server, you should set $WebPath to the path you'll be
+# serving RT at.
+# $WebPath requires a leading / but no trailing /.
+#
+# In most cases, you should leave $WebPath set to '' (an empty value).
-=item C<$WebPort>
+Set($WebPath , "");
-If we're running as a superuser, run on port 80
-Otherwise, pick a high port for this user.
+# If we're running as a superuser, run on port 80
+# Otherwise, pick a high port for this user.
-443 is default port for https protocol.
-
-=cut
+Set($WebPort , 80);# + ($< * 7274) % 32766 + ($< && 1024));
-Set($WebPort, 80);# + ($< * 7274) % 32766 + ($< && 1024));
+# This is the Scheme, server and port for constructing urls to webrt
+# $WebBaseURL doesn't need a trailing /
-=item C<$WebPath>
+Set($WebBaseURL , "http://localhost:$WebPort");
-If you're putting the web ui somewhere other than at the root of
-your server, you should set C<$WebPath> to the path you'll be
-serving RT at.
+Set($WebURL , $WebBaseURL . $WebPath . "/");
-C<$WebPath> requires a leading / but no trailing /, or it can be blank.
+# $WebImagesURL points to the base URL where RT can find its images.
-In most cases, you should leave C<$WebPath> set to '' (an empty value).
-
-=cut
-
-Set($WebPath, "");
-
-=item C<$WebBaseURL>, C<$WebURL>
-
-Usually you don't want to set these options. The only obviouse reason is
-RT accessible via https protocol on non standard port, eg
-'https://rt.example.com:9999'. In all other cases these options are computed
-using C<$WebDomain>, C<$WebPort> and C<$WebPath>.
-
-C<$WebBaseURL> is the scheme, server and port (eg 'http://rt.example.com')
-for constructing urls to the web UI. C<$WebBaseURL> doesn't need a trailing /.
-
-C<$WebURL> is the C<$WebBaseURL>, C<$WebPath> and trailing /, for example:
-'http://www.example.com/rt/'.
-
-=cut
+Set($WebImagesURL , $WebPath . "/NoAuth/images/");
-my $port = RT->Config->Get('WebPort');
-Set($WebBaseURL,
- ($port == 443? 'https': 'http') .'://'
- . RT->Config->Get('WebDomain')
- . ($port != 80 && $port != 443? ":$port" : '')
-);
+# $LogoURL points to the URL of the RT logo displayed in the web UI
-Set($WebURL, RT->Config->Get('WebBaseURL') . RT->Config->Get('WebPath') . "/");
+Set($LogoURL , $WebImagesURL . "bplogo.gif");
-=item C<$WebImagesURL>
+# WebNoAuthRegex - What portion of RT's URLspace should not require
+# authentication.
+Set($WebNoAuthRegex, qr!^/rt(?:/+NoAuth/|
+ /+REST/\d+\.\d+/NoAuth/)!x );
-C<$WebImagesURL> points to the base URL where RT can find its images.
-Define the directory name to be used for images in rt web
-documents.
-
-=cut
-
-Set($WebImagesURL, RT->Config->Get('WebPath') . "/NoAuth/images/");
-
-=item C<$LogoURL>
-
-C<$LogoURL> points to the URL of the RT logo displayed in the web UI
-
-=cut
-
-Set($LogoURL, RT->Config->Get('WebImagesURL') . "bplogo.gif");
-
-=item C<$WebNoAuthRegex>
-
-What portion of RT's URL space should not require authentication.
-
-This is mostly for extension and doesn't mean RT will work without
-login if you change it.
-
-=cut
-
-Set($WebNoAuthRegex, qr{^ (?:/+NoAuth/ | /+REST/\d+\.\d+/NoAuth/) }x );
-
-=item C<$SelfServiceRegex>
-
-What portion of RT's URLspace should be accessible to Unprivileged users
-This does not override the redirect from F</Ticket/Display.html> to
-F</SelfService/Display.html> when Unprivileged users attempt to access
-ticked displays
-
-=cut
-
-Set($SelfServiceRegex, qr!^(?:/+SelfService/)!x );
-
-=item C<$MessageBoxWidth>, C<$MessageBoxHeight>
-
-For message boxes, set the entry box width, height and what type of
-wrapping to use. These options can be overridden by users in their
-preferences.
-
-Default width: 72, height: 15
-
-These settings only apply to the non-RichText message box.
-See below for Rich Text settings.
-
-=cut
-
-Set($MessageBoxWidth, 72);
-Set($MessageBoxHeight, 15);
-
-=item C<$MessageBoxWrap>
-
-Default wrapping: "HARD" (choices "SOFT", "HARD")
-
-Wrapping is disabled when using MessageBoxRichText because
-of a bad interaction between IE and wrapping with the Rich
-Text Editor.
-
-=cut
+# For message boxes, set the entry box width and what type of wrapping
+# to use.
+#
+# Default width: 72
+Set($MessageBoxWidth , 72);
+# Default wrapping: "HARD" (choices "SOFT", "HARD")
Set($MessageBoxWrap, "HARD");
-=item C<$MessageBoxRichText>
-
-Should "rich text" editing be enabled? This option lets your users send html email messages from the web interface.
-
-=cut
-
-Set($MessageBoxRichText, 1);
-
-=item C<$MessageBoxRichTextHeight>
-
-Height of RichText javascript enabled editing boxes (in pixels)
-
-=cut
-
-Set($MessageBoxRichTextHeight, 200);
-
-=item C<$MessageBoxIncludeSignature>
-
-Should your user's signatures (from their Preferences page) be included in Comments and Replies
-
-=cut
-
-Set($MessageBoxIncludeSignature, 1);
-
-=item C<$WikiImplicitLinks>
-
-Support implicit links in WikiText custom fields? A true value
-causes InterCapped or ALLCAPS words in WikiText fields to
-automatically become links to searches for those words. If used on
-RTFM articles, it links to the RTFM article with that name.
-
-=cut
-
+# Support implicit links in WikiText custom fields? A true value
+# causes InterCapped or ALLCAPS words in WikiText fields to
+# automatically become links to searches for those words. If used on
+# RTFM articles, it links to the RTFM article with that name.
Set($WikiImplicitLinks, 0);
-=item C<$TrustHTMLAttachments>
-
-if C<TrustHTMLAttachments> is not defined, we will display them
-as text. This prevents malicious HTML and javascript from being
-sent in a request (although there is probably more to it than that)
-
-=cut
-
-Set($TrustHTMLAttachments, undef);
-
-=item C<$RedistributeAutoGeneratedMessages>
-
-Should RT redistribute correspondence that it identifies as
-machine generated? A true value will do so; setting this to '0'
-will cause no such messages to be redistributed.
-You can also use 'privileged' (the default), which will redistribute
-only to privileged users. This helps to protect against malformed
-bounces and loops caused by autocreated requestors with bogus addresses.
-
-=cut
-
+# if TrustHTMLAttachments is not defined, we will display them
+# as text. This prevents malicious HTML and javascript from being
+# sent in a request (although there is probably more to it than that)
+Set($TrustHTMLAttachments , undef);
+
+# Should RT redistribute correspondence that it identifies as
+# machine generated? A true value will do so; setting this to '0'
+# will cause no such messages to be redistributed.
+# You can also use 'privileged' (the default), which will redistribute
+# only to privileged users. This helps to protect against malformed
+# bounces and loops caused by autocreated requestors with bogus addresses.
Set($RedistributeAutoGeneratedMessages, 'privileged');
-=item C<$PreferRichText>
-
-If C<$PreferRichText> is set to a true value, RT will show HTML/Rich text
-messages in preference to their plaintext alternatives. RT "scrubs" the
-html to show only a minimal subset of HTML to avoid possible contamination
-by cross-site-scripting attacks.
-
-=cut
-
+# If PreferRichText is set to a true value, RT will show HTML/Rich text
+# messages in preference to their plaintext alternatives. RT "scrubs" the
+# html to show only a minimal subset of HTML to avoid possible contamination
+# by cross-site-scripting attacks.
Set($PreferRichText, undef);
-=item C<$WebExternalAuth>
-
-If C<$WebExternalAuth> is defined, RT will defer to the environment's
-REMOTE_USER variable.
-
-=cut
-
-Set($WebExternalAuth, undef);
+# If $WebExternalAuth is defined, RT will defer to the environment's
+# REMOTE_USER variable.
-=item C<$WebExternalAuthContinuous>
+Set($WebExternalAuth , undef);
-If C<$WebExternalAuthContinuous> is defined, RT will check for the
-REMOTE_USER on each access. If you would prefer this to only happen
-once (at initial login) set this to a false value. The default setting
-will help ensure that if your external auth system deauthenticates a
-user, RT notices as soon as possible.
-
-=cut
-
-Set($WebExternalAuthContinuous, 1);
-
-=item C<$WebFallbackToInternalAuth>
-
-If C<$WebFallbackToInternalAuth> is defined, the user is allowed a chance
-of fallback to the login screen, even if REMOTE_USER failed.
-
-=cut
+# If $WebFallbackToInternalAuth is undefined, the user is allowed a chance
+# of fallback to the login screen, even if REMOTE_USER failed.
Set($WebFallbackToInternalAuth , undef);
-=item C<$WebExternalGecos>
-
-C<$WebExternalGecos> means to match 'gecos' field as the user identity);
-useful with mod_auth_pwcheck and IIS Integrated Windows logon.
-
-=cut
+# $WebExternalGecos means to match 'gecos' field as the user identity);
+# useful with mod_auth_pwcheck and IIS Integrated Windows logon.
Set($WebExternalGecos , undef);
-=item C<$WebExternalAuto>
-
-C<$WebExternalAuto> will create users under the same name as REMOTE_USER
-upon login, if it's missing in the Users table.
-
-=cut
+# $WebExternalAuto will create users under the same name as REMOTE_USER
+# upon login, if it's missing in the Users table.
Set($WebExternalAuto , undef);
-=item C<$AutoCreate>
-
-If C<$WebExternalAuto> is true, C<$AutoCreate> will be passed to User's
-Create method. Use it to set defaults, such as creating
-Unprivileged users with C<{ Privileged => 0 }>
-( Must be a hashref of arguments )
-
-=cut
-
-Set($AutoCreate, undef);
-
-=item C<$WebSessionClass>
-
-C<$WebSessionClass> is the class you wish to use for managing Sessions.
-It defaults to use your SQL database, but if you are using MySQL 3.x and
-plans to use non-ascii Queue names, uncomment and add this line to
-F<RT_SiteConfig.pm> will prevent session corruption.
-
-=cut
+# $WebSessionClass is the class you wish to use for managing Sessions.
+# It defaults to use your SQL database, but if you are using MySQL 3.x and
+# plans to use non-ascii Queue names, uncomment and add this line to
+# RT_SiteConfig.pm will prevent session corruption.
# Set($WebSessionClass , 'Apache::Session::File');
-=item C<$AutoLogoff>
-
-By default, RT's user sessions persist until a user closes his or her
-browser. With the C<$AutoLogoff> option you can setup session lifetime in
-minutes. A user will be logged out if he or she doesn't send any requests
-to RT for the defined time.
-
-=cut
-Set($AutoLogoff, 0);
-
-=item C<$WebSecureCookies>
-
-By default, RT's session cookie isn't marked as "secure" Some web browsers
-will treat secure cookies more carefully than non-secure ones, being careful
-not to write them to disk, only send them over an SSL secured connection
-and so on. To enable this behaviour, set C<$WebSecureCookies> to a true value.
-NOTE: You probably don't want to turn this on _unless_ users are only connecting
-via SSL encrypted HTTP connections.
-
-=cut
+# By default, RT's session cookie isn't marked as "secure" Some web browsers
+# will treat secure cookies more carefully than non-secure ones, being careful
+# not to write them to disk, only send them over an SSL secured connection
+# and so on. To enable this behaviour, set # $WebSecureCookies to a true value.
+# NOTE: You probably don't want to turn this on _unless_ users are only connecting
+# via SSL encrypted HTTP connections.
Set($WebSecureCookies, 0);
-=item C<$WebFlushDbCacheEveryRequest>
-By default, RT clears its database cache after every page view.
-This ensures that you've always got the most current information
-when working in a multi-process (mod_perl or FastCGI) Environment
-Setting C<$WebFlushDbCacheEveryRequest> to '0' will turn this off,
-which will speed RT up a bit, at the expense of a tiny bit of data
-accuracy.
-
-=cut
+# By default, RT clears its database cache after every page view.
+# This ensures that you've always got the most current information
+# when working in a multi-process (mod_perl or FastCGI) Environment
+# Setting $WebFlushDbCacheEveryRequest to '0' will turn this off,
+# which will speed RT up a bit, at the expense of a tiny bit of data
+# accuracy.
Set($WebFlushDbCacheEveryRequest, '1');
-=item C<$MaxInlineBody>
-
-C<$MaxInlineBody> is the maximum attachment size that we want to see
-inline when viewing a transaction. RT will inline any text if value
-is undefined or 0. This option can be overridden by users in their
-preferences.
-
-=cut
-
-Set($MaxInlineBody, 12000);
-
-=item C<$DefaultSummaryRows>
+# $MaxInlineBody is the maximum attachment size that we want to see
+# inline when viewing a transaction. 13456 is a random sane-sounding
+# default.
-C<$DefaultSummaryRows> is default number of rows displayed in for search
-results on the frontpage.
+Set($MaxInlineBody, 13456);
-=cut
+# $DefaultSummaryRows is default number of rows displayed in for search
+# results on the frontpage.
Set($DefaultSummaryRows, 10);
-=item C<$HomePageRefreshInterval>
-
-C<$HomePageRefreshInterval> is default number of seconds to refresh the RT
-home page. Choose from [0, 120, 300, 600, 1200, 3600, 7200].
-
-=cut
-
-Set($HomePageRefreshInterval, 0);
-
-=item C<$SearchResultsRefreshInterval>
-
-C<$SearchResultsRefreshInterval> is default number of seconds to refresh
-search results in RT. Choose from [0, 120, 300, 600, 1200, 3600, 7200].
-
-=cut
-
-Set($SearchResultsRefreshInterval, 0);
-
-=item C<$OldestTransactionsFirst>
-
-By default, RT shows newest transactions at the bottom of the ticket
-history page, if you want see them at the top set this to '0'. This
-option can be overridden by users in their preferences.
-
-=cut
+# By default, RT shows newest transactions at the bottom of the ticket
+# history page, if you want see them at the top set this to '0'.
Set($OldestTransactionsFirst, '1');
-=item C<$ShowTransactionImages>
-
-By default, RT shows images attached to incoming (and outgoing) ticket updates
-inline. Set this variable to 0 if you'd like to disable that behaviour
-
-=cut
+# By default, RT shows images attached to incoming (and outgoing) ticket updates
+# inline. Set this variable to 0 if you'd like to disable that behaviour
Set($ShowTransactionImages, 1);
-=item C<$PlainTextPre>
-
-Normally plaintext attachments are displayed as HTML with line
-breaks preserved. This causes space- and tab-based formatting not
-to be displayed correctly. By setting $PlainTextPre they'll be
-displayed using <pre> instead so such formatting works, but they'll
-use a monospaced font, no matter what the value of C<$PlainTextMono> is.
-
-=cut
-
-Set($PlainTextPre, 0);
-
-
-=item C<$PlainTextMono>
-To display plaintext attachments,
-Set C<$PlainTextMono> to 1 to use monospaced font and preserve
-formatting, but unlike PlainTextPre, the text will wrap to fit into the
-UI.
-
-=cut
-
-Set($PlainTextMono, 0);
-
-=item C<$ShowUnreadMessageNotifications>
-
-By default, RT will prompt users when there are new, unread messages on
-tickets they are viewing.
-
-Set C<$ShowUnreadMessageNotifications> to a false value to disable this feature.
-
-=cut
-
-Set($ShowUnreadMessageNotifications, 1);
-
-
-=item C<$HomepageComponents>
-
-C<$HomepageComponents> is an arrayref of allowed components on a user's
-customized homepage ("RT at a glance").
-
-=cut
-
-Set($HomepageComponents, [qw(QuickCreate Quicksearch MyAdminQueues MySupportQueues MyReminders RefreshHomepage Dashboards)]);
-
-=item C<@MasonParameters>
-C<@MasonParameters> is the list of parameters for the constructor of
-HTML::Mason's Apache or CGI Handler. This is normally only useful
-for debugging, eg. profiling individual components with:
+# $HomepageComponents is an arrayref of allowed components on a user's
+# customized homepage ("RT at a glance").
- use MasonX::Profiler; # available on CPAN
- Set(@MasonParameters, (preamble => 'my $p = MasonX::Profiler->new($m, $r);'));
-
-=cut
+Set($HomepageComponents, [qw(QuickCreate Quicksearch MyAdminQueues MySupportQueues MyReminders RefreshHomepage)]);
-Set(@MasonParameters, ());
+# @MasonParameters is the list of parameters for the constructor of
+# HTML::Mason's Apache or CGI Handler. This is normally only useful
+# for debugging, eg. profiling individual components with:
+# use MasonX::Profiler; # available on CPAN
+# @MasonParameters = (preamble => 'my $p = MasonX::Profiler->new($m, $r);');
-=item C<$DefaultSearchResultFormat>
-
-C<$DefaultSearchResultFormat> is the default format for RT search results
-
-=cut
+@MasonParameters = () unless (@MasonParameters);
+# $DefaultSearchResultFormat is the default format for RT search results
Set ($DefaultSearchResultFormat, qq{
- '<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__id__</a></B>/TITLE:#',
- '<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
+ '<B><A HREF="$RT::WebPath/Ticket/Display.html?id=__id__">__id__</a></B>/TITLE:#',
+ '<B><A HREF="$RT::WebPath/Ticket/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
Status,
QueueName,
OwnerName,
@@ -1237,387 +493,95 @@ Set ($DefaultSearchResultFormat, qq{
'<small>__LastUpdatedRelative__</small>',
'<small>__TimeLeft__</small>'});
-=item C<$DefaultSelfServiceSearchResultFormat>
-
-C<$DefaultSelfServiceSearchResultFormat> is the default format of searches displayed in the
-SelfService interface.
-
-=cut
-
-Set($DefaultSelfServiceSearchResultFormat, qq{
- '<B><A HREF="__WebPath__/SelfService/Display.html?id=__id__">__id__</a></B>/TITLE:#',
- '<B><A HREF="__WebPath__/SelfService/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
- Status,
- Requestors,
- OwnerName});
-
-=item C<$SuppressInlineTextFiles>
-
-If C<$SuppressInlineTextFiles> is set to a true value, then uploaded
-text files (text-type attachments with file names) are prevented
-from being displayed in-line when viewing a ticket's history.
-
-=cut
+# If $SuppressInlineTextFiles is set to a true value, then uploaded
+# text files (text-type attachments with file names) are prevented
+# from being displayed in-line when viewing a ticket's history.
Set($SuppressInlineTextFiles, undef);
-=item C<$DontSearchFileAttachments>
-
-If C<$DontSearchFileAttachments> is set to a true value, then uploaded
-files (attachments with file names) are not searched during full-content
-ticket searches.
-
-=cut
+# If $DontSearchFileAttachments is set to a true value, then uploaded
+# files (attachments with file names) are not searched during full-content
+# ticket searches.
Set($DontSearchFileAttachments, undef);
-=item C<$ChartFont>
-
-The L<GD> module (which RT uses for graphs) uses a builtin font that doesn't
-have full Unicode support. You can use a particular TrueType font by setting
-$ChartFont to the absolute path of that font. Your GD library must have
-support for TrueType fonts to use this option.
-
-=cut
-
-Set($ChartFont, undef);
-
-
-=item C<@Active_MakeClicky>
-
-MakeClicky detects various formats of data in headers and email
-messages, and extends them with supporting links. By default, RT
-provides two formats:
-
-* 'httpurl': detects http:// and https:// URLs and adds '[Open URL]'
- link after the URL.
-
-* 'httpurl_overwrite': also detects URLs as 'httpurl' format, but
- replace URL with link and *adds spaces* into text if it's longer
- then 30 chars. This allow browser to wrap long URLs and avoid
- horizontal scrolling.
-
-See F<share/html/Elements/MakeClicky> for documentation on how to add your own.
-
-=cut
-
-Set(@Active_MakeClicky, qw());
-
-=item C<$DefaultQueue>
-
-Use this to select the default queue name that will be used for creating new
-tickets. You may use either the queue's name or its ID. This only affects the
-queue selection boxes on the web interface.
-
-=cut
-
-#Set($DefaultQueue, 'General');
-
-=item C<$DefaultTimeUnitsToHours>
-
-Use this to set the default units for time entry to hours instead of minutes.
-
-=cut
-
-Set($DefaultTimeUnitsToHours, 0);
-
-=back
-
-=head1 L<Net::Server> (rt-server) Configuration
-
-=over 4
-
-=item C<$StandaloneMinServers>, C<$StandaloneMaxServers>
-
-The absolute minimum and maximum number of servers that will be created to
-handle requests. Having multiple servers means that serving a slow page will
-affect other users less.
-
-=cut
-
-Set($StandaloneMinServers, 1);
-Set($StandaloneMaxServers, 1);
-
-=item C<$StandaloneMinSpareServers>, C<$StandaloneMaxSpareServers>
-These next two options can be used to scale up and down the number of servers
-to adjust to load. These two options will respect the C<$StandaloneMinServers
-> and C<$StandaloneMaxServers options>.
+# }}}
-=cut
-
-Set($StandaloneMinSpareServers, 0);
-Set($StandaloneMaxSpareServers, 0);
-
-=item C<$StandaloneMaxRequests>
-
-This sets the absolute maximum number of requests a single server will serve.
-Setting this would be useful if, for example, memory usage slowly crawls up
-every hit.
-
-=cut
-
-#Set($StandaloneMaxRequests, 50);
-
-=item C<%NetServerOptions>
-
-C<%NetServerOptions> is a hash of additional options to use for
-L<Net::Server/DEFAULT ARGUMENTS>. For example, you could set
-reverse_lookups to get the hostnames for all users with:
-
-C<Set(%NetServerOptions, (reverse_lookups => 1));>
-
-=cut
-
-Set(%NetServerOptions, ());
-
-=back
-
-
-=head1 UTF-8 Configuration
-
-=over 4
-
-=item C<@LexiconLanguages>
-
-An array that contains languages supported by RT's internationalization
-interface. Defaults to all *.po lexicons; setting it to C<qw(en ja)> will make
-RT bilingual instead of multilingual, but will save some memory.
+# {{{ RT UTF-8 Settings
-=cut
-
-Set(@LexiconLanguages, qw(*));
-
-=item C<@EmailInputEncodings>
-
-An array that contains default encodings used to guess which charset
-an attachment uses if not specified. Must be recognized by
-L<Encode::Guess>.
-
-=cut
-
-Set(@EmailInputEncodings, qw(utf-8 iso-8859-1 us-ascii));
-
-=item C<$EmailOutputEncoding>
+# An array that contains languages supported by RT's internationalization
+# interface. Defaults to all *.po lexicons; setting it to qw(en ja) will make
+# RT bilingual instead of multilingual, but will save some memory.
-The charset for localized email. Must be recognized by Encode.
-
-=cut
+@LexiconLanguages = qw(*) unless (@LexiconLanguages);
-Set($EmailOutputEncoding, 'utf-8');
+# An array that contains default encodings used to guess which charset
+# an attachment uses if not specified. Must be recognized by
+# Encode::Guess.
+@EmailInputEncodings = qw(utf-8 iso-8859-1 us-ascii) unless (@EmailInputEncodings);
-=back
+# The charset for localized email. Must be recognized by Encode.
-=head1 Date Handling Configuration
+Set($EmailOutputEncoding , 'utf-8');
-=over 4
+# }}}
-=item C<$DateTimeFormat>
+# {{{ RT Date Handling Options (for Time::ParseDate)
-You can choose date and time format. See "Output formatters"
-section in perldoc F<lib/RT/Date.pm> for more options. This option can
-be overridden by users in their preferences.
-Some examples:
-
-C<Set($DateTimeFormat, 'LocalizedDateTime');>
-C<Set($DateTimeFormat, { Format => 'ISO', Seconds => 0 });>
-C<Set($DateTimeFormat, 'RFC2822');>
-C<Set($DateTimeFormat, { Format => 'RFC2822', Seconds => 0, DayOfWeek => 0 });>
-
-=cut
-
-Set($DateTimeFormat, 'DefaultFormat');
-
-# Next two options are for Time::ParseDate
-
-=item C<$DateDayBeforeMonth>
-
-Set this to 1 if your local date convention looks like "dd/mm/yy" instead of
-"mm/dd/yy". Used only for parsing, not for displaying dates.
-
-=cut
+# Set this to 1 if your local date convention looks like "dd/mm/yy"
+# instead of "mm/dd/yy".
Set($DateDayBeforeMonth , 1);
-=item C<$AmbiguousDayInPast>, C<$AmbiguousDayInFuture>
+# Should "Tuesday" default to meaning "Next Tuesday" or "Last Tuesday"?
+# Set to 0 for "Next" or 1 for "Last".
-Should an unspecified day or year in a date refer to a future or a
-past value? For example, should a date of "Tuesday" default to mean
-the date for next Tuesday or last Tuesday? Should the date "March 1"
-default to the date for next March or last March?
+Set($AmbiguousDayInPast , 1);
-Set $<AmbiguousDayInPast> for the last date, or $<$AmbiguousDayInFuture> for the
-next date.
+# }}}
-The default is usually good.
+# {{{ Miscellaneous RT Settings
-=cut
-
-Set($AmbiguousDayInPast, 0);
-Set($AmbiguousDayInFuture, 0);
-
-=back
-
-=head1 Approval Configuration
-
-Configration for the approvl system
-
-=over 4
-
-=item C<$ApprovalRejectionNotes>
-
-Should rejection notes be sent to the requestors? The default is true.
-
-=cut
-
-Set($ApprovalRejectionNotes, 1);
-
-
-=back
-
-=head1 Miscellaneous Configuration
-
-=over 4
-
-=item C<@ActiveStatus>, C<@InactiveStatus>
-
-You can define new statuses and even reorder existing statuses here.
-WARNING. DO NOT DELETE ANY OF THE DEFAULT STATUSES. If you do, RT
-will break horribly. The statuses you add must be no longer than
-10 characters.
-
-=cut
+# You can define new statuses and even reorder existing statuses here.
+# WARNING. DO NOT DELETE ANY OF THE DEFAULT STATUSES. If you do, RT
+# will break horribly. The statuses you add must be no longer than
+# 10 characters.
-Set(@ActiveStatus, qw(new open stalled));
-Set(@InactiveStatus, qw(resolved rejected deleted));
+@ActiveStatus = qw(new open stalled) unless @ActiveStatus;
+@InactiveStatus = qw(resolved rejected deleted) unless @InactiveStatus;
-=item C<$LinkTransactionsRun1Scrip>
-
-RT-3.4 backward compatibility setting. Add/Delete Link used to record one
-transaction and run one scrip. Set this value to 1 if you want
-only one of the link transactions to have scrips run.
-
-=cut
-
-Set($LinkTransactionsRun1Scrip, 0);
-
-=item C<$StrictLinkACL>
-
-When this feature is enabled a user needs I<ModifyTicket> rights on both
-tickets to link them together, otherwise he can have rights on either of
-them.
-
-=cut
+# Backward compatability setting. Add/Delete Link used to record one
+# transaction and run one scrip. Set this value to 1 if you want
+# only one of the link transactions to have scrips run.
+Set($LinkTransactionsRun1Scrip , 0);
+# When this feature is enabled an user need ModifyTicket right on both
+# tickets to link them together, otherwise he can have right on any of
+# two.
Set($StrictLinkACL, 1);
-=item C<$PreviewScripMessages>
-
-Set C<$PreviewScripMessages> to 1 if the scrips preview on the ticket
-reply page should include the content of the messages to be sent.
-
-=cut
-
-Set($PreviewScripMessages, 0);
-
-=item C<$UseTransactionBatch>
-
-Set C<$UseTransactionBatch> to 1 to execute transactions in batches,
-such that a resolve and comment (for example) would happen
-simultaneously, instead of as two transactions, unaware of each
-others' existence.
-
-=cut
-
-Set($UseTransactionBatch, 1);
-
-=item C<@CustomFieldValuesSources>
-
-Set C<@CustomFieldValuesSources> to a list of class names which extend
-L<RT::CustomFieldValues::External>. This can be used to pull lists of
-custom field values from external sources at runtime.
+# }}}
-=cut
-
-Set(@CustomFieldValuesSources, ());
-
-=item C<$CanonicalizeRedirectURLs>
-
-Set C<$CanonicalizeRedirectURLs> to 1 to use $C<WebURL> when redirecting rather
-than the one we get from C<%ENV>.
-
-If you use RT behind a reverse proxy, you almost certainly want to
-enable this option.
-
-=cut
-
-Set($CanonicalizeRedirectURLs, 0);
-=item C<$EnableReminders>
-
-Hide links/portlets related to Reminders by setting this to 0
-
-=cut
-
-Set($EnableReminders,1);
-
-
-=item C<@Plugins>
-
-Set C<@Plugins> to a list of external RT plugins that should be enabled (those
-plugins have to be previously downloaded and installed).
-Example:
-
-C<Set(@Plugins, (qw(Extension::QuickDelete RT::FM)));>
-
-=cut
-
-Set(@Plugins, ());
-=back
-
-=head1 Development Configuration
-
-=over 4
-
-=item C<$DevelMode>
-
-RT comes with a "Development mode" setting.
-This setting, as a convenience for developers, turns on
-all sorts of development options that you most likely don't want in
-production:
-
-* Turns off Mason's 'static_source' directive. By default, you can't
- edit RT's web ui components on the fly and have RT magically pick up
- your changes. (It's a big performance hit)
-
- * More to come
-
-=cut
+# {{{ Development Mode
+#
+# RT comes with a "Development mode" setting.
+# This setting, as a convenience for developers, turns on
+# all sorts of development options that you most likely don't want in
+# production:
+#
+# * Turns off Mason's 'static_source' directive. By default, you can't
+# edit RT's web ui components on the fly and have RT magically pick up
+# your changes. (It's a big performance hit)
+#
+# * More to come
+#
Set($DevelMode, '0');
+# }}}
-=back
-
-=head1 Deprecated Options
-
-=over 4
-
-=item C<$AlwaysUseBase64>
-
-Encode blobs as base64 in DB (?)
-
-=item C<$TicketBaseURI>
-
-Base URI to tickets in this system; used when loading (?)
-
-=item C<$UseCodeTickets>
-
-This option is exists for backwards compatibility. Don't use it.
-
-=back
-
-=cut
1;
diff --git a/rt/etc/RT_Config.pm.in b/rt/etc/RT_Config.pm.in
index 5d28e719c..37a2c17bb 100644
--- a/rt/etc/RT_Config.pm.in
+++ b/rt/etc/RT_Config.pm.in
@@ -1,3 +1,7 @@
+#
+# WARNING: NEVER EDIT RT_Config.pm. Instead, copy any sections you want to change to RT_SiteConfig.pm
+# and edit them there.
+#
package RT;
@@ -11,1220 +15,486 @@ use RT::Config;
=cut
-=head1 WARNING
+# {{{ Base Configuration
-NEVER EDIT RT_Config.pm.
+# $rtname is the string that RT will look for in mail messages to
+# figure out what ticket a new piece of mail belongs to
-Instead, copy any sections you want to change to F<RT_SiteConfig.pm> and edit them there.
-
-=cut
-
-=head1 Base Configuration
-
-=over 4
-
-=item C<$rtname>
-
-C<$rtname> is the string that RT will look for in mail messages to
-figure out what ticket a new piece of mail belongs to.
-
-Your domain name is recommended, so as not to pollute the namespace.
-once you start using a given tag, you should probably never change it.
-(otherwise, mail for existing tickets won't get put in the right place)
-
-=cut
+# Your domain name is recommended, so as not to pollute the namespace.
+# once you start using a given tag, you should probably never change it.
+# (otherwise, mail for existing tickets won't get put in the right place
Set($rtname , "example.com");
-=item C<$EmailSubjectTagRegex>
-
-This regexp controls what subject tags RT recognizes as its own.
-If you're not dealing with historical C<$rtname> values, you'll likely
-never have to enable this feature.
-
-Be VERY CAREFUL with it. Note that it overrides C<$rtname> for subject
-token matching and that you should use only "non-capturing" parenthesis
-grouping. For example:
-
-C<Set($EmailSubjectTagRegex, qr/(?:example.com|example.org)/i );>
-
-and NOT
-
-C<Set($EmailSubjectTagRegex, qr/(example.com|example.org)/i );>
-
-This setting would make RT behave exactly as it does without the
-setting enabled.
-
-=cut
-
-#Set($EmailSubjectTagRegex, qr/\Q$rtname\E/i );
-
+# This regexp controls what subject tags RT recognizes as its own.
+# If you're not dealing with historical $rtname values, you'll likely
+# never have to enable this feature.
+#
+# Be VERY CAREFUL with it. Note that it overrides $rtname for subject
+# token matching and that you should use only "non-capturing" parenthesis
+# grouping. For example:
+#
+# Set($EmailSubjectTagRegex, qr/(?:example.com|example.org)/i );
+#
+# and NOT
+#
+# Set($EmailSubjectTagRegex, qr/(example.com|example.org)/i );
+#
+# This setting would make RT behave exactly as it does without the
+# setting enabled.
+#
+# Set($EmailSubjectTagRegex, qr/\Q$rtname\E/i );
-=item C<$Organization>
-You should set this to your organization's DNS domain. For example,
-I<fsck.com> or I<asylum.arkham.ma.us>. It's used by the linking interface to
-guarantee that ticket URIs are unique and easy to construct.
-
-=cut
+# You should set this to your organization's DNS domain. For example,
+# fsck.com or asylum.arkham.ma.us. It's used by the linking interface to
+# guarantee that ticket URIs are unique and easy to construct.
Set($Organization , "example.com");
-=item C<$MinimumPasswordLength>
-
-C<$MinimumPasswordLength> defines the minimum length for user
-passwords. Setting it to 0 disables this check.
-
-=cut
-
+# $user_passwd_min defines the minimum length for user passwords. Setting
+# it to 0 disables this check
Set($MinimumPasswordLength , "5");
-=item C<$Timezone>
-
-C<$Timezone> is used to convert times entered by users into GMT and back again
-It should be set to a timezone recognized by your local unix box.
-
-=cut
-
+# $Timezone is used to convert times entered by users into GMT and back again
+# It should be set to a timezone recognized by your local unix box.
Set($Timezone , 'US/Eastern');
-=back
+# }}}
-=head1 Database Configuration
+# {{{ Database Configuration
-=over 4
-
-=item C<$DatabaseType>
-
-Database driver being used; case matters.
-
-Valid types are "mysql", "Oracle" and "Pg"
-
-=cut
+# Database driver beeing used. Case matters
+# Valid types are "mysql", "Oracle" and "Pg"
Set($DatabaseType , '@DB_TYPE@');
-=item C<$DatabaseHost>, C<$DatabaseRTHost>
-
-The domain name of your database server.
-
-If you're running mysql and it's on localhost,
-leave it blank for enhanced performance
-
-=cut
-
+# The domain name of your database server
+# If you're running mysql and it's on localhost,
+# leave it blank for enhanced performance
Set($DatabaseHost , '@DB_HOST@');
Set($DatabaseRTHost , '@DB_RT_HOST@');
-=item C<$DatabasePort>
-
-The port that your database server is running on. Ignored unless it's
-a positive integer. It's usually safe to leave this blank
-
-=cut
-
+# The port that your database server is running on. Ignored unless it's
+# a positive integer. It's usually safe to leave this blank
Set($DatabasePort , '@DB_PORT@');
-=item C<$DatabaseUser>
-
-The name of the database user (inside the database)
-
-=cut
-
+#The name of the database user (inside the database)
Set($DatabaseUser , '@DB_RT_USER@');
-=item C<$DatabasePassword>
-
-Password the C<$DatabaseUser> should use to access the database
-
-=cut
-
+# Password the DatabaseUser should use to access the database
Set($DatabasePassword , '@DB_RT_PASS@');
-=item C<$DatabaseName>
-
-The name of the RT's database on your database server. For Oracle
-it's SID, DB objects are created in L<$DatabaseUser>'s schema.
-
-=cut
-
+# The name of the RT's database on your database server
Set($DatabaseName , '@DB_DATABASE@');
-=item C<$DatabaseRequireSSL>
-
-If you're using Postgres and have compiled in SSL support,
-set C<$DatabaseRequireSSL> to 1 to turn on SSL communication
-
-=cut
-
+# If you're using Postgres and have compiled in SSL support,
+# set DatabaseRequireSSL to 1 to turn on SSL communication
Set($DatabaseRequireSSL , undef);
-=item C<$UseSQLForACLChecks>
-
-In RT for ages ACL are checked after search what in some situtations
-result in empty search pages and wrong count of tickets.
-
-Set C<$UseSQLForACLChecks> to 1 to use SQL and get rid of these problems.
-
-However, this option is beta. In some cases it result in performance
-improvements, but some setups can not handle it.
-
-=cut
-
-Set($UseSQLForACLChecks, undef);
-
-=back
-
-=head1 Incoming Mail Gateway Configuration
+# }}}
-=over 4
+# {{{ Incoming mail gateway configuration
-=item C<$OwnerEmail>
-
-C<$OwnerEmail> is the address of a human who manages RT. RT will send
-errors generated by the mail gateway to this address. This address
-should _not_ be an address that's managed by your RT instance.
-
-=cut
+# OwnerEmail is the address of a human who manages RT. RT will send
+# errors generated by the mail gateway to this address. This address
+# should _not_ be an address that's managed by your RT instance.
Set($OwnerEmail , 'root');
-=item C<$LoopsToRTOwner>
-
-If C<$LoopsToRTOwner> is defined, RT will send mail that it believes
-might be a loop to C<$OwnerEmail>
-
-=cut
+# If $LoopsToRTOwner is defined, RT will send mail that it believes
+# might be a loop to $RT::OwnerEmail
Set($LoopsToRTOwner , 1);
-=item C<$StoreLoops>
-
-If C<$StoreLoops> is defined, RT will record messages that it believes
-to be part of mail loops.
-
-As it does this, it will try to be careful not to send mail to the
-sender of these messages
-
-=cut
+# If $StoreLoops is defined, RT will record messages that it believes
+# to be part of mail loops.
+# As it does this, it will try to be careful not to send mail to the
+# sender of these messages
Set($StoreLoops , undef);
-=item C<$MaxAttachmentSize>
-
-C<$MaxAttachmentSize> sets the maximum size (in bytes) of attachments stored
-in the database.
-
-For mysql and oracle, we set this size at 10 megabytes.
-If you're running a postgres version earlier than 7.1, you will need
-to drop this to 8192. (8k)
-
-=cut
+# $MaxAttachmentSize sets the maximum size (in bytes) of attachments stored
+# in the database.
+# For mysql and oracle, we set this size at 10 megabytes.
+# If you're running a postgres version earlier than 7.1, you will need
+# to drop this to 8192. (8k)
Set($MaxAttachmentSize , 10000000);
-=item C<$TruncateLongAttachments>
-
-C<$TruncateLongAttachments>: if this is set to a non-undef value,
-RT will truncate attachments longer than C<$MaxAttachmentSize>.
-
-=cut
+# $TruncateLongAttachments: if this is set to a non-undef value,
+# RT will truncate attachments longer than MaxAttachmentSize.
Set($TruncateLongAttachments , undef);
-=item C<$DropLongAttachments>
-
-C<$DropLongAttachments>: if this is set to a non-undef value,
-RT will silently drop attachments longer than C<MaxAttachmentSize>.
-
-=cut
+# $DropLongAttachments: if this is set to a non-undef value,
+# RT will silently drop attachments longer than MaxAttachmentSize.
Set($DropLongAttachments , undef);
-=item C<$ParseNewMessageForTicketCcs>
-
-If C<$ParseNewMessageForTicketCcs> is true, RT will attempt to divine
-Ticket 'Cc' watchers from the To and Cc lines of incoming messages
-Be forewarned that if you have _any_ addresses which forward mail to
-RT automatically and you enable this option without modifying
-C<$RTAddressRegexp> below, you will get yourself into a heap of trouble.
-
-=cut
+# If $ParseNewMessageForTicketCcs is true, RT will attempt to divine
+# Ticket 'Cc' watchers from the To and Cc lines of incoming messages
+# Be forewarned that if you have _any_ addresses which forward mail to
+# RT automatically and you enable this option without modifying
+# "RTAddressRegexp" below, you will get yourself into a heap of trouble.
Set($ParseNewMessageForTicketCcs , undef);
-=item C<$RTAddressRegexp>
-
-C<$RTAddressRegexp> is used to make sure RT doesn't add itself as a ticket CC if
-the setting above is enabled. It is important that you set this to a
-regular expression that matches all addresses used by your RT. This lets RT
-avoid sending mail to itself. It will also hide RT addresses from the list of
-"One-time Cc" and Bcc lists on ticket reply.
-
-=cut
+# RTAddressRegexp is used to make sure RT doesn't add itself as a ticket CC if
+# the setting above is enabled.
Set($RTAddressRegexp , '^rt\@example.com$');
-=item C<$CanonicalizeEmailAddressMatch>, C<$CanonicalizeEmailAddressReplace>
-
-RT provides functionality which allows the system to rewrite
-incoming email addresses. In its simplest form,
-you can substitute the value in $<CanonicalizeEmailAddressReplace>
-for the value in $<CanonicalizeEmailAddressMatch>
-(These values are passed to the $<CanonicalizeEmailAddress> subroutine in
- F<RT/User.pm>)
-
-By default, that routine performs a C<s/$Match/$Replace/gi> on any address
-passed to it.
-
-=cut
+# RT provides functionality which allows the system to rewrite
+# incoming email addresses. In its simplest form,
+# you can substitute the value in CanonicalizeEmailAddressReplace
+# for the value in CanonicalizeEmailAddressMatch
+# (These values are passed to the CanonicalizeEmailAddress subroutine in RT/User.pm)
+# By default, that routine performs a s/$Match/$Replace/gi on any address passed to it
#Set($CanonicalizeEmailAddressMatch , '@subdomain\.example\.com$');
#Set($CanonicalizeEmailAddressReplace , '@example.com');
-=item C<$CanonicalizeEmailAddressMatch>
-
-Set this to true and the create new user page will use the values that you
-enter in the form but use the function CanonicalizeUserInfo in
-F<RT/User_Local.pm>
-
-=cut
-
-Set($CanonicalizeOnCreate, 0);
-
-=item C<$SenderMustExistInExternalDatabase>
-
-If C<$SenderMustExistInExternalDatabase> is true, RT will refuse to
-create non-privileged accounts for unknown users if you are using
-the C<$LookupSenderInExternalDatabase> option.
-Instead, an error message will be mailed and RT will forward the
-message to C<$RTOwner>.
-
-If you are not using C<$LookupSenderInExternalDatabase>, this option
-has no effect.
-
-If you define an AutoRejectRequest template, RT will use this
-template for the rejection message.
-
-=cut
+# set this to true and the create new user page will use the values that you
+# enter in the form but use the function CanonicalizeUserInfo in User_Local.pm
+Set($CanonicalizeOnCreate , 0);
+
+# If $SenderMustExistInExternalDatabase is true, RT will refuse to
+# create non-privileged accounts for unknown users if you are using
+# the "LookupSenderInExternalDatabase" option.
+# Instead, an error message will be mailed and RT will forward the
+# message to $RTOwner.
+#
+# If you are not using $LookupSenderInExternalDatabase, this option
+# has no effect.
+#
+# If you define an AutoRejectRequest template, RT will use this
+# template for the rejection message.
Set($SenderMustExistInExternalDatabase , undef);
-=item C<$ValidateUserEmailAddresses>
+# }}}
-If C<$ValidateUserEmailAddresses> is true, RT will refuse to create users with
-an invalid email address (as specified in RFC 2822) or with an email address
-made of multiple email adresses.
+# {{{ Outgoing mail configuration
-=cut
+# RT is designed such that any mail which already has a ticket-id associated
+# with it will get to the right place automatically.
-Set($ValidateUserEmailAddresses, undef);
+# $CorrespondAddress and $CommentAddress are the default addresses
+# that will be listed in From: and Reply-To: headers of correspondence
+# and comment mail tracked by RT, unless overridden by a queue-specific
+# address.
-=item C<@MailPlugins>
+Set($CorrespondAddress , 'RT_CorrespondAddressNotSet');
-C<@MailPlugins> is a list of auth plugins for L<RT::Interface::Email>
-to use; see L<rt-mailgate>
-
-=cut
-
-=item C<$UnsafeEmailCommands>
-
-C<$UnsafeEmailCommands>, if set to true, enables 'take' and 'resolve'
-as possible actions via the mail gateway. As its name implies, this
-is very unsafe, as it allows email with a forged sender to possibly
-resolve arbitrary tickets!
-
-=cut
+Set($CommentAddress , 'RT_CommentAddressNotSet');
-=item C<$ExtractSubjectTagMatch>, C<$ExtractSubjectTagNoMatch>
+#Sendmail Configuration
-The default "extract remote tracking tags" scrip settings; these
-detect when your RT is talking to another RT, and adjusts the
-subject accordingly.
-
-=cut
-
-Set($ExtractSubjectTagMatch, qr/\[.+? #\d+\]/);
-Set($ExtractSubjectTagNoMatch, ( ${RT::EmailSubjectTagRegex}
- ? qr/\[(?:${RT::EmailSubjectTagRegex}) #\d+\]/
- : qr/\[\Q$RT::rtname\E #\d+\]/));
-
-=back
-
-=head1 Outgoing Mail Configuration
-
-=over 4
-
-=item C<$MailCommand>
-
-C<$MailCommand> defines which method RT will use to try to send mail.
-We know that 'sendmailpipe' works fairly well. If 'sendmailpipe'
-doesn't work well for you, try 'sendmail'. Other options are 'smtp'
-or 'qmail'.
-
-Note that you should remove the '-t' from C<$SendmailArguments>
-if you use 'sendmail' rather than 'sendmailpipe'
-
-=cut
+# $MailCommand defines which method RT will use to try to send mail
+# We know that 'sendmailpipe' works fairly well.
+# If 'sendmailpipe' doesn't work well for you, try 'sendmail'
+#
+# Note that you should remove the '-t' from $SendmailArguments
+# if you use 'sendmail' rather than 'sendmailpipe'
Set($MailCommand , 'sendmailpipe');
-=item C<$SetOutgoingMailFrom>
-
-C<$SetOutgoingMailFrom> tells RT to set the sender envelope with the correspond
-mail address of the ticket's queue.
-
-Warning: If you use this setting, bounced mails will appear to be incoming
-mail to the system, thus creating new tickets.
-
-=cut
-
-Set($SetOutgoingMailFrom, 0);
-
-=item C<$OverrideOutgoingMailFrom>
-
-C<$OverrideOutgoingMailFrom> is used for overwriting the Correspond
-address of the queue. The option is a hash reference of queue name to
-email address.
-
-If there is no ticket involved, then the value of the C<Default> key will be
-used.
-
-=cut
-
-Set($OverrideOutgoingMailFrom, {
-# 'Default' => 'admin@rt.example.com',
-# 'General' => 'general@rt.example.com',
-});
-
-=back
-
-=head1 Sendmail Configuration
-
-These options only take effect if C<$MailCommand> is 'sendmail' or
-'sendmailpipe'
-
-=over 4
-
-=item C<$SendmailArguments>
-
-C<$SendmailArguments> defines what flags to pass to C<$SendmailPath>
-If you picked 'sendmailpipe', you MUST add a -t flag to C<$SendmailArguments>
-These options are good for most sendmail wrappers and workalikes
-
-These arguments are good for sendmail brand sendmail 8 and newer
-C<Set($SendmailArguments,"-oi -t -ODeliveryMode=b -OErrorMode=m");>
-
-=cut
+# $SendmailArguments defines what flags to pass to $Sendmail
+# assuming you picked 'sendmail' or 'sendmailpipe' as the $MailCommand above.
+# If you picked 'sendmailpipe', you MUST add a -t flag to $SendmailArguments
+# These options are good for most sendmail wrappers and workalikes
Set($SendmailArguments , "-oi -t");
-
-=item C<$SendmailBounceArguments>
-
-C<$SendmailBounceArguments> defines what flags to pass to C<$Sendmail>
-assuming RT needs to send an error (ie. bounce).
-
-=cut
+# $SendmailBounceArguments defines what flags to pass to $Sendmail
+# assuming RT needs to send an error (ie. bounce).
Set($SendmailBounceArguments , '-f "<>"');
-=item C<$SendmailPath>
-
-If you selected 'sendmailpipe' above, you MUST specify the path to
-your sendmail binary in C<$SendmailPath>.
-
-=cut
+# These arguments are good for sendmail brand sendmail 8 and newer
+#Set($SendmailArguments,"-oi -t -ODeliveryMode=b -OErrorMode=m");
+# If you selected 'sendmailpipe' above, you MUST specify the path
+# to your sendmail binary in $SendmailPath.
+# !! If you did not # select 'sendmailpipe' above, this has no effect!!
Set($SendmailPath , "/usr/sbin/sendmail");
+# By default, RT sets the outgoing mail's "From:" header to
+# "SenderName via RT". Setting this option to 0 disables it.
-=back
-
-=head1 SMTP Configuration
-
-These options only take effect if C<$MailCommand> is 'smtp'
-
-=over 4
-
-=item C<$SMTPServer>
-
-C<$SMTPServer> should be set to the hostname of the SMTP server to use
-
-=cut
-
-Set($SMTPServer, undef);
-
-=item C<$SMTPFrom>
-
-C<$SMTPFrom> should be set to the 'From' address to use, if not the
-email's 'From'
-
-=cut
-
-Set($SMTPFrom, undef);
-
-=item C<$SMTPDebug>
-
-C<$SMTPDebug> should be set to true to debug SMTP mail sending
-
-=cut
-
-Set($SMTPDebug, 0);
-
-=back
-
-=head1 Other Mailer Configuration
-
-=over 4
-
-=item C<@MailParams>
-
-C<@MailParams> defines a list of options passed to $MailCommand if it
-is not 'sendmailpipe', 'sendmail', or 'smtp'
-
-=cut
-
-Set(@MailParams, ());
-
-=item C<$CorrespondAddress>, C<$CommentAddress>
-
-RT is designed such that any mail which already has a ticket-id associated
-with it will get to the right place automatically.
-
-C<$CorrespondAddress> and C<$CommentAddress> are the default addresses
-that will be listed in From: and Reply-To: headers of correspondence
-and comment mail tracked by RT, unless overridden by a queue-specific
-address.
-
-=cut
-
-Set($CorrespondAddress , '');
-
-Set($CommentAddress , '');
-
-=item C<$DashboardAddress>
-
-The email address from which RT will send dashboards. If none is set, then
-C<$OwnerEmail> will be used.
-
-=cut
-
-Set($DashboardAddress, '');
-
-=item C<$UseFriendlyFromLine>
-
-By default, RT sets the outgoing mail's "From:" header to
-"SenderName via RT". Setting C<$UseFriendlyFromLine> to 0 disables it.
-
-=cut
-
-Set($UseFriendlyFromLine, 1);
-
-=item C<$FriendlyFromLineFormat>
-
-C<sprintf()> format of the friendly 'From:' header; its arguments
-are SenderName and SenderEmailAddress.
-
-=cut
+Set($UseFriendlyFromLine , 1);
-Set($FriendlyFromLineFormat, "\"%s via RT\" <%s>");
+# sprintf() format of the friendly 'From:' header; its arguments
+# are SenderName and SenderEmailAddress.
+Set($FriendlyFromLineFormat , "\"%s via RT\" <%s>");
-=item C<$UseFriendlyToLine>
-
-RT can optionally set a "Friendly" 'To:' header when sending messages to
-Ccs or AdminCcs (rather than having a blank 'To:' header.
-
-This feature DOES NOT WORK WITH SENDMAIL[tm] BRAND SENDMAIL
-If you are using sendmail, rather than postfix, qmail, exim or some other MTA,
-you _must_ disable this option.
-
-=cut
-
-Set($UseFriendlyToLine, 0);
-
-=item C<$FriendlyToLineFormat>
-
-C<sprintf()> format of the friendly 'From:' header; its arguments
-are WatcherType and TicketId.
-
-=cut
+# RT can optionally set a "Friendly" 'To:' header when sending messages to
+# Ccs or AdminCcs (rather than having a blank 'To:' header.
-Set($FriendlyToLineFormat, "\"%s of ". RT->Config->Get('rtname') ." Ticket #%s\":;");
+# This feature DOES NOT WORK WITH SENDMAIL[tm] BRAND SENDMAIL
+# If you are using sendmail, rather than postfix, qmail, exim or some other MTA,
+# you _must_ disable this option.
-=item C<$NotifyActor>
+Set($UseFriendlyToLine , 0);
-By default, RT doesn't notify the person who performs an update, as they
-already know what they've done. If you'd like to change this behaviour,
-Set C<$NotifyActor> to 1
+# sprintf() format of the friendly 'From:' header; its arguments
+# are WatcherType and TicketId.
+Set($FriendlyToLineFormat, "\"%s of $RT::rtname Ticket #%s\":;");
-=cut
+# By default, RT doesn't notify the person who performs an update, as they
+# already know what they've done. If you'd like to change this behaviour,
+# Set $NotifyActor to 1
Set($NotifyActor, 0);
-=item C<$RecordOutgoingEmail>
-
-By default, RT records each message it sends out to its own internal database.
-To change this behavior, set C<$RecordOutgoingEmail> to 0
-
-=cut
+# By default, RT records each message it sends out to its own internal database.# To change this behaviour, set $RecordOutgoingEmail to 0
Set($RecordOutgoingEmail, 1);
-=item C<$VERPPrefix>, C<$VERPPrefix>
-
-VERP support (http://cr.yp.to/proto/verp.txt)
-
-uncomment the following two directives to generate envelope senders
-of the form C<${VERPPrefix}${originaladdress}@${VERPDomain}>
-(i.e. rt-jesse=fsck.com@rt.example.com ).
-
-This currently only works with sendmail and sendmailppie.
-
-=cut
-
+# VERP support (http://cr.yp.to/proto/verp.txt)
+# uncomment the following two directives to generate envelope senders
+# of the form ${VERPPrefix}${originaladdress}@${VERPDomain}
+# (i.e. rt-jesse=fsck.com@rt.example.com ) This currently only works
+# with sendmail and sendmailppie.
# Set($VERPPrefix, 'rt-');
# Set($VERPDomain, $RT::Organization);
+# }}}
-=item C<$ForwardFromUser>
+# {{{ Logging
-By default, RT forwards a message using queue's address and adds RT's tag into
-subject of the outgoing message, so recipients' replies go into RT as correspondents.
-
-To change this behavior, set C<$ForwardFromUser> to true value and RT will use
-address of the current user and leave subject without RT's tag.
-
-=cut
-
-Set($ForwardFromUser, 0);
-
-=item C<$ShowBccHeader>
-
-By default RT hides from the web UI information about blind copies user sent on
-reply or comment.
-
-To change this set the following option to true value.
-
-=cut
+# Logging. The default is to log anything except debugging
+# information to syslog. Check the Log::Dispatch POD for
+# information about how to get things by syslog, mail or anything
+# else, get debugging info in the log, etc.
-Set($ShowBccHeader, 0);
+# It might generally make
+# sense to send error and higher by email to some administrator.
+# If you do this, be careful that this email isn't sent to this RT instance.
-=item C<$DashboardSubject>
-
-Lets you set the subject of dashboards. Arguments are the frequency (Daily,
-Weekly, Monthly) of the dashboard and the dashboard's name. [_1] for the name
-of the dashboard.
-
-=cut
-
-Set($DashboardSubject, '%s Dashboard: %s');
-
-=back
-
-=head1 GnuPG Configuration
-
-A full description of the (somewhat extensive) GnuPG integration can be found
-by running the command `perldoc L<RT::Crypt::GnuPG>` (or `perldoc
- lib/RT/Crypt/GnuPG.pm` from your RT install directory).
-
-=over 4
-
-=item C<%GnuPG>
-
-Set C<OutgoingMessagesFormat> to 'inline' to use inline encryption and
-signatures instead of 'RFC' (GPG/MIME: RFC3156 and RFC1847) format.
-
-If you want to allow people to encrypt attachments inside the DB then
-set C<AllowEncryptDataInDB> to true
-
-Set C<RejectOnMissingPrivateKey> to false if you don't want to reject
-emails encrypted for key RT doesn't have and can not decrypt.
-
-Set C<RejectOnBadData> to false if you don't want to reject letters
-with incorrect GnuPG data.
-
-=cut
-
-Set( %GnuPG,
- Enable => @RT_GPG@,
- OutgoingMessagesFormat => 'RFC', # Inline
- AllowEncryptDataInDB => 0,
-
- RejectOnMissingPrivateKey => 1,
- RejectOnBadData => 1,
-);
-
-=item C<%GnuPGOptions>
-
-Options of GnuPG program.
-
-If you override this in your RT_SiteConfig, you should be sure
-to include a homedir setting.
-
-NOTE that options with '-' character MUST be quoted.
-
-=cut
-
-Set(%GnuPGOptions,
- homedir => '@RT_VAR_PATH@/data/gpg',
-
-# URL of a keyserver
-# keyserver => 'hkp://subkeys.pgp.net',
-
-# enables the automatic retrieving of keys when encrypting
-# 'auto-key-locate' => 'keyserver',
-
-# enables the automatic retrieving of keys when verifying signatures
-# 'auto-key-retrieve' => undef,
-);
-
-
-=back
-
-=head1 Logging Configuration
-
-The default is to log anything except debugging
-information to syslog. Check the L<Log::Dispatch> POD for
-information about how to get things by syslog, mail or anything
-else, get debugging info in the log, etc.
-
-It might generally make sense to send error and higher by email to
-some administrator. If you do this, be careful that this email
-isn't sent to this RT instance. Mail loops will generate a critical
-log message.
-
-=over 4
-
-=item C<$LogToSyslog>, C<$LogToScreen>
-
-The minimum level error that will be logged to the specific device.
-From lowest to highest priority, the levels are:
- debug info notice warning error critical alert emergency
-
-=cut
-
-Set($LogToSyslog , 'info');
-Set($LogToScreen , 'info');
-
-=item C<$LogToFile>, C<$LogDir>, C<$LogToFileNamed>
-
-Logging to a standalone file is also possible, but note that the
-file should needs to both exist and be writable by all direct users
-of the RT API. This generally include the web server, whoever
-rt-crontool runs as. Note that as rt-mailgate and the RT CLI go
-through the webserver, so their users do not need to have write
-permissions to this file. If you expect to have multiple users of
-the direct API, Best Practical recommends using syslog instead of
-direct file logging.
-
-=cut
+# the minimum level error that will be logged to the specific device.
+# levels from lowest to highest:
+# debug info notice warning error critical alert emergency
+# Mail loops will generate a critical log message.
+Set($LogToSyslog , 'debug');
+Set($LogToScreen , 'error');
Set($LogToFile , undef);
Set($LogDir, '@RT_LOG_PATH@');
Set($LogToFileNamed , "rt.log"); #log to rt.log
-=item C<$LogStackTraces>
-
-If set to a log level then logging will include stack traces for
-messages with level equal to or greater than specified.
+# If true generates stack traces to file log or screen
+# never generates traces to syslog
-NOTICE: Stack traces include parameters supplied to functions or
-methods. It is possible for stack trace logging to reveal sensitive
-information such as passwords or ticket content in your logs.
+Set($LogStackTraces , 0);
-=cut
-
-Set($LogStackTraces, '');
-
-=item C<@LogToSyslogConf>
+# On Solaris or UnixWare, set to ( socket => 'inet' ). Options here
+# override any other options RT passes to Log::Dispatch::Syslog.
+# Other interesting flags include facility and logopt. (See the
+# Log::Dispatch::Syslog documentation for more information.) (Maybe
+# ident too, if you have multiple RT installations.)
-On Solaris or UnixWare, set to ( socket => 'inet' ). Options here
-override any other options RT passes to L<Log::Dispatch::Syslog>.
-Other interesting flags include facility and logopt. (See the
-L<Log::Dispatch::Syslog> documentation for more information.) (Maybe
-ident too, if you have multiple RT installations.)
-
-=cut
-
-Set(@LogToSyslogConf, ());
-
-=item C<$StatementLog>,
-
-RT has rudimentary SQL statement logging support if you have
-DBIx-SearchBuilder 1.31_1 or higher; simply set C<$StatementLog> to be
-the level that you wish SQL statements to be logged at.
-
-=cut
+@LogToSyslogConf = () unless (@LogToSyslogConf);
+# RT has rudimentary SQL statement logging support if you have
+# DBIx-SearchBuilder 1.31_1 or higher; simply set $StatementLog to be
+# the level that you wish SQL statements to be logged at.
Set($StatementLog, undef);
-=back
-
-=head1 Web Interface Configuration
-
-=over 4
-
-=item C<$WebDefaultStylesheet>
-
-This determines the default stylesheet the RT web interface will use.
-RT ships with several themes by default:
-
- web2 The totally new, default layout for RT 3.8
- 3.5-default RT 3.5 and 3.6 original layout
- 3.4-compat A 3.4 compatibility stylesheet to make RT look
- (mostly) like 3.4
-
-This value actually specifies a directory in F<share/html/NoAuth/css/>
-from which RT will try to load the file main.css (which should
-@import any other files the stylesheet needs). This allows you to
-easily and cleanly create your own stylesheets to apply to RT. This
-option can be overridden by users in their preferences.
-
-=cut
-
-Set($WebDefaultStylesheet, 'web2');
-
-=item C<$UsernameFormat>
-
-This determines how user info is displayed. 'concise' will show one of
-either NickName, RealName, Name or EmailAddress, depending on what exists
-and whether the user is privileged or not. 'verbose' will show RealName and
-EmailAddress.
-
-=cut
-
-Set($UsernameFormat, 'concise');
-
-=item C<$WebDomain>
-
-Domain name of the RT server, eg 'www.example.com'. It should not contain
-anything else, but server name.
-
-=cut
-
-Set( $WebDomain, 'localhost' );
-
-=item C<$WebPort>
-
-If we're running as a superuser, run on port 80
-Otherwise, pick a high port for this user.
-
-443 is default port for https protocol.
+# }}}
-=cut
-
-Set($WebPort, 80);# + ($< * 7274) % 32766 + ($< && 1024));
-
-=item C<$WebPath>
-
-If you're putting the web ui somewhere other than at the root of
-your server, you should set C<$WebPath> to the path you'll be
-serving RT at.
-
-C<$WebPath> requires a leading / but no trailing /, or it can be blank.
-
-In most cases, you should leave C<$WebPath> set to '' (an empty value).
-
-=cut
-
-Set($WebPath, "");
-
-=item C<$WebBaseURL>, C<$WebURL>
+# {{{ Web interface configuration
-Usually you don't want to set these options. The only obviouse reason is
-RT accessible via https protocol on non standard port, eg
-'https://rt.example.com:9999'. In all other cases these options are computed
-using C<$WebDomain>, C<$WebPort> and C<$WebPath>.
+# This determines the default stylesheet the RT web interface will use.
+# RT ships with two valid values by default:
+#
+# 3.5-default The totally new, default layout for RT 3.5
+# 3.4-compat A 3.4 compatibility stylesheet to make RT 3.5 look
+# (mostly) like 3.4
+#
+# This value actually specifies a directory in share/html/NoAuth/css/
+# from which RT will try to load the file main.css (which should
+# @import any other files the stylesheet needs). This allows you to
+# easily and cleanly create your own stylesheets to apply to RT.
-C<$WebBaseURL> is the scheme, server and port (eg 'http://rt.example.com')
-for constructing urls to the web UI. C<$WebBaseURL> doesn't need a trailing /.
-
-C<$WebURL> is the C<$WebBaseURL>, C<$WebPath> and trailing /, for example:
-'http://www.example.com/rt/'.
-
-=cut
-
-my $port = RT->Config->Get('WebPort');
-Set($WebBaseURL,
- ($port == 443? 'https': 'http') .'://'
- . RT->Config->Get('WebDomain')
- . ($port != 80 && $port != 443? ":$port" : '')
-);
-
-Set($WebURL, RT->Config->Get('WebBaseURL') . RT->Config->Get('WebPath') . "/");
-
-=item C<$WebImagesURL>
-
-C<$WebImagesURL> points to the base URL where RT can find its images.
-Define the directory name to be used for images in rt web
-documents.
-
-=cut
+Set($WebDefaultStylesheet, '3.5-default');
-Set($WebImagesURL, RT->Config->Get('WebPath') . "/NoAuth/images/");
+# Define the directory name to be used for images in rt web
+# documents.
-=item C<$LogoURL>
+# If you're putting the web ui somewhere other than at the root of
+# your server, you should set $WebPath to the path you'll be
+# serving RT at.
+# $WebPath requires a leading / but no trailing /.
+#
+# In most cases, you should leave $WebPath set to '' (an empty value).
-C<$LogoURL> points to the URL of the RT logo displayed in the web UI
+Set($WebPath , "");
-=cut
+# If we're running as a superuser, run on port 80
+# Otherwise, pick a high port for this user.
-Set($LogoURL, RT->Config->Get('WebImagesURL') . "bplogo.gif");
+Set($WebPort , 80);# + ($< * 7274) % 32766 + ($< && 1024));
-=item C<$WebNoAuthRegex>
+# This is the Scheme, server and port for constructing urls to webrt
+# $WebBaseURL doesn't need a trailing /
-What portion of RT's URL space should not require authentication.
+Set($WebBaseURL , "http://localhost:$WebPort");
-This is mostly for extension and doesn't mean RT will work without
-login if you change it.
+Set($WebURL , $WebBaseURL . $WebPath . "/");
-=cut
+# $WebImagesURL points to the base URL where RT can find its images.
-Set($WebNoAuthRegex, qr{^ (?:/+NoAuth/ | /+REST/\d+\.\d+/NoAuth/) }x );
+Set($WebImagesURL , $WebPath . "/NoAuth/images/");
-=item C<$SelfServiceRegex>
+# $LogoURL points to the URL of the RT logo displayed in the web UI
-What portion of RT's URLspace should be accessible to Unprivileged users
-This does not override the redirect from F</Ticket/Display.html> to
-F</SelfService/Display.html> when Unprivileged users attempt to access
-ticked displays
+Set($LogoURL , $WebImagesURL . "bplogo.gif");
-=cut
+# WebNoAuthRegex - What portion of RT's URLspace should not require
+# authentication.
+Set($WebNoAuthRegex, qr!^/rt(?:/+NoAuth/|
+ /+REST/\d+\.\d+/NoAuth/)!x );
+# SelfServiceRegex - What portion of RT's URLspace should
+# be accessible to Unprivileged users
+# This does not override the redirect from /Ticket/Display.html
+# to /SelfService/Display.html when Unprivileged
+# users attempt to access ticked displays
Set($SelfServiceRegex, qr!^(?:/+SelfService/)!x );
-=item C<$MessageBoxWidth>, C<$MessageBoxHeight>
-
-For message boxes, set the entry box width, height and what type of
-wrapping to use. These options can be overridden by users in their
-preferences.
-
-Default width: 72, height: 15
-
-These settings only apply to the non-RichText message box.
-See below for Rich Text settings.
-
-=cut
-
-Set($MessageBoxWidth, 72);
-Set($MessageBoxHeight, 15);
-
-=item C<$MessageBoxWrap>
-
-Default wrapping: "HARD" (choices "SOFT", "HARD")
-
-Wrapping is disabled when using MessageBoxRichText because
-of a bad interaction between IE and wrapping with the Rich
-Text Editor.
-
-=cut
+# For message boxes, set the entry box width and what type of wrapping
+# to use.
+#
+# Default width: 72
+Set($MessageBoxWidth , 72);
+# Default wrapping: "HARD" (choices "SOFT", "HARD")
Set($MessageBoxWrap, "HARD");
-=item C<$MessageBoxRichText>
-
-Should "rich text" editing be enabled? This option lets your users send html email messages from the web interface.
-
-=cut
-
-Set($MessageBoxRichText, 1);
-
-=item C<$MessageBoxRichTextHeight>
-
-Height of RichText javascript enabled editing boxes (in pixels)
-
-=cut
-
-Set($MessageBoxRichTextHeight, 200);
-
-=item C<$MessageBoxIncludeSignature>
-
-Should your user's signatures (from their Preferences page) be included in Comments and Replies
-
-=cut
-
-Set($MessageBoxIncludeSignature, 1);
-
-=item C<$WikiImplicitLinks>
-
-Support implicit links in WikiText custom fields? A true value
-causes InterCapped or ALLCAPS words in WikiText fields to
-automatically become links to searches for those words. If used on
-RTFM articles, it links to the RTFM article with that name.
-
-=cut
-
+# Support implicit links in WikiText custom fields? A true value
+# causes InterCapped or ALLCAPS words in WikiText fields to
+# automatically become links to searches for those words. If used on
+# RTFM articles, it links to the RTFM article with that name.
Set($WikiImplicitLinks, 0);
-=item C<$TrustHTMLAttachments>
-
-if C<TrustHTMLAttachments> is not defined, we will display them
-as text. This prevents malicious HTML and javascript from being
-sent in a request (although there is probably more to it than that)
-
-=cut
-
-Set($TrustHTMLAttachments, undef);
-
-=item C<$RedistributeAutoGeneratedMessages>
-
-Should RT redistribute correspondence that it identifies as
-machine generated? A true value will do so; setting this to '0'
-will cause no such messages to be redistributed.
-You can also use 'privileged' (the default), which will redistribute
-only to privileged users. This helps to protect against malformed
-bounces and loops caused by autocreated requestors with bogus addresses.
-
-=cut
-
+# if TrustHTMLAttachments is not defined, we will display them
+# as text. This prevents malicious HTML and javascript from being
+# sent in a request (although there is probably more to it than that)
+Set($TrustHTMLAttachments , undef);
+
+# Should RT redistribute correspondence that it identifies as
+# machine generated? A true value will do so; setting this to '0'
+# will cause no such messages to be redistributed.
+# You can also use 'privileged' (the default), which will redistribute
+# only to privileged users. This helps to protect against malformed
+# bounces and loops caused by autocreated requestors with bogus addresses.
Set($RedistributeAutoGeneratedMessages, 'privileged');
-=item C<$PreferRichText>
-
-If C<$PreferRichText> is set to a true value, RT will show HTML/Rich text
-messages in preference to their plaintext alternatives. RT "scrubs" the
-html to show only a minimal subset of HTML to avoid possible contamination
-by cross-site-scripting attacks.
-
-=cut
-
+# If PreferRichText is set to a true value, RT will show HTML/Rich text
+# messages in preference to their plaintext alternatives. RT "scrubs" the
+# html to show only a minimal subset of HTML to avoid possible contamination
+# by cross-site-scripting attacks.
Set($PreferRichText, undef);
-=item C<$WebExternalAuth>
+# If $WebExternalAuth is defined, RT will defer to the environment's
+# REMOTE_USER variable.
-If C<$WebExternalAuth> is defined, RT will defer to the environment's
-REMOTE_USER variable.
-
-=cut
+Set($WebExternalAuth , undef);
-Set($WebExternalAuth, undef);
-
-=item C<$WebExternalAuthContinuous>
-
-If C<$WebExternalAuthContinuous> is defined, RT will check for the
-REMOTE_USER on each access. If you would prefer this to only happen
-once (at initial login) set this to a false value. The default setting
-will help ensure that if your external auth system deauthenticates a
-user, RT notices as soon as possible.
-
-=cut
-
-Set($WebExternalAuthContinuous, 1);
-
-=item C<$WebFallbackToInternalAuth>
-
-If C<$WebFallbackToInternalAuth> is defined, the user is allowed a chance
-of fallback to the login screen, even if REMOTE_USER failed.
-
-=cut
+# If $WebFallbackToInternalAuth is undefined, the user is allowed a chance
+# of fallback to the login screen, even if REMOTE_USER failed.
Set($WebFallbackToInternalAuth , undef);
-=item C<$WebExternalGecos>
-
-C<$WebExternalGecos> means to match 'gecos' field as the user identity);
-useful with mod_auth_pwcheck and IIS Integrated Windows logon.
-
-=cut
+# $WebExternalGecos means to match 'gecos' field as the user identity);
+# useful with mod_auth_pwcheck and IIS Integrated Windows logon.
Set($WebExternalGecos , undef);
-=item C<$WebExternalAuto>
-
-C<$WebExternalAuto> will create users under the same name as REMOTE_USER
-upon login, if it's missing in the Users table.
-
-=cut
+# $WebExternalAuto will create users under the same name as REMOTE_USER
+# upon login, if it's missing in the Users table.
Set($WebExternalAuto , undef);
-=item C<$AutoCreate>
-
-If C<$WebExternalAuto> is true, C<$AutoCreate> will be passed to User's
-Create method. Use it to set defaults, such as creating
-Unprivileged users with C<{ Privileged => 0 }>
-( Must be a hashref of arguments )
-
-=cut
+# If $WebExternalAuto is true, this will be passed to User's
+# Create method. Use it to set defaults, such as creating
+# Unprivileged users with { Privileged => 0 }
+# Must be a hashref of arguments
Set($AutoCreate, undef);
-=item C<$WebSessionClass>
-
-C<$WebSessionClass> is the class you wish to use for managing Sessions.
-It defaults to use your SQL database, but if you are using MySQL 3.x and
-plans to use non-ascii Queue names, uncomment and add this line to
-F<RT_SiteConfig.pm> will prevent session corruption.
-
-=cut
+# $WebSessionClass is the class you wish to use for managing Sessions.
+# It defaults to use your SQL database, but if you are using MySQL 3.x and
+# plans to use non-ascii Queue names, uncomment and add this line to
+# RT_SiteConfig.pm will prevent session corruption.
# Set($WebSessionClass , 'Apache::Session::File');
-=item C<$AutoLogoff>
-
-By default, RT's user sessions persist until a user closes his or her
-browser. With the C<$AutoLogoff> option you can setup session lifetime in
-minutes. A user will be logged out if he or she doesn't send any requests
-to RT for the defined time.
-
-=cut
-Set($AutoLogoff, 0);
-
-=item C<$WebSecureCookies>
-
-By default, RT's session cookie isn't marked as "secure" Some web browsers
-will treat secure cookies more carefully than non-secure ones, being careful
-not to write them to disk, only send them over an SSL secured connection
-and so on. To enable this behaviour, set C<$WebSecureCookies> to a true value.
-NOTE: You probably don't want to turn this on _unless_ users are only connecting
-via SSL encrypted HTTP connections.
-
-=cut
+# By default, RT's session cookie isn't marked as "secure" Some web browsers
+# will treat secure cookies more carefully than non-secure ones, being careful
+# not to write them to disk, only send them over an SSL secured connection
+# and so on. To enable this behaviour, set # $WebSecureCookies to a true value.
+# NOTE: You probably don't want to turn this on _unless_ users are only connecting
+# via SSL encrypted HTTP connections.
Set($WebSecureCookies, 0);
-=item C<$WebFlushDbCacheEveryRequest>
-By default, RT clears its database cache after every page view.
-This ensures that you've always got the most current information
-when working in a multi-process (mod_perl or FastCGI) Environment
-Setting C<$WebFlushDbCacheEveryRequest> to '0' will turn this off,
-which will speed RT up a bit, at the expense of a tiny bit of data
-accuracy.
-
-=cut
+# By default, RT clears its database cache after every page view.
+# This ensures that you've always got the most current information
+# when working in a multi-process (mod_perl or FastCGI) Environment
+# Setting $WebFlushDbCacheEveryRequest to '0' will turn this off,
+# which will speed RT up a bit, at the expense of a tiny bit of data
+# accuracy.
Set($WebFlushDbCacheEveryRequest, '1');
-=item C<$MaxInlineBody>
-
-C<$MaxInlineBody> is the maximum attachment size that we want to see
-inline when viewing a transaction. RT will inline any text if value
-is undefined or 0. This option can be overridden by users in their
-preferences.
-
-=cut
-
-Set($MaxInlineBody, 12000);
-
-=item C<$DefaultSummaryRows>
+# $MaxInlineBody is the maximum attachment size that we want to see
+# inline when viewing a transaction. 13456 is a random sane-sounding
+# default.
-C<$DefaultSummaryRows> is default number of rows displayed in for search
-results on the frontpage.
+Set($MaxInlineBody, 13456);
-=cut
+# $DefaultSummaryRows is default number of rows displayed in for search
+# results on the frontpage.
Set($DefaultSummaryRows, 10);
-=item C<$HomePageRefreshInterval>
-
-C<$HomePageRefreshInterval> is default number of seconds to refresh the RT
-home page. Choose from [0, 120, 300, 600, 1200, 3600, 7200].
-
-=cut
-
-Set($HomePageRefreshInterval, 0);
-
-=item C<$SearchResultsRefreshInterval>
-
-C<$SearchResultsRefreshInterval> is default number of seconds to refresh
-search results in RT. Choose from [0, 120, 300, 600, 1200, 3600, 7200].
-
-=cut
-
-Set($SearchResultsRefreshInterval, 0);
-
-=item C<$OldestTransactionsFirst>
-
-By default, RT shows newest transactions at the bottom of the ticket
-history page, if you want see them at the top set this to '0'. This
-option can be overridden by users in their preferences.
-
-=cut
+# By default, RT shows newest transactions at the bottom of the ticket
+# history page, if you want see them at the top set this to '0'.
Set($OldestTransactionsFirst, '1');
-=item C<$ShowTransactionImages>
-
-By default, RT shows images attached to incoming (and outgoing) ticket updates
-inline. Set this variable to 0 if you'd like to disable that behaviour
-
-=cut
+# By default, RT shows images attached to incoming (and outgoing) ticket updates
+# inline. Set this variable to 0 if you'd like to disable that behaviour
Set($ShowTransactionImages, 1);
-=item C<$PlainTextPre>
-
-Normally plaintext attachments are displayed as HTML with line
-breaks preserved. This causes space- and tab-based formatting not
-to be displayed correctly. By setting $PlainTextPre they'll be
-displayed using <pre> instead so such formatting works, but they'll
-use a monospaced font, no matter what the value of C<$PlainTextMono> is.
-
-=cut
-
-Set($PlainTextPre, 0);
-
-
-=item C<$PlainTextMono>
-To display plaintext attachments,
-Set C<$PlainTextMono> to 1 to use monospaced font and preserve
-formatting, but unlike PlainTextPre, the text will wrap to fit into the
-UI.
-
-=cut
-
-Set($PlainTextMono, 0);
-
-=item C<$ShowUnreadMessageNotifications>
-
-By default, RT will prompt users when there are new, unread messages on
-tickets they are viewing.
-
-Set C<$ShowUnreadMessageNotifications> to a false value to disable this feature.
-
-=cut
-Set($ShowUnreadMessageNotifications, 1);
+# $HomepageComponents is an arrayref of allowed components on a user's
+# customized homepage ("RT at a glance").
+Set($HomepageComponents, [qw(QuickCreate Quicksearch MyAdminQueues MySupportQueues MyReminders RefreshHomepage)]);
-=item C<$HomepageComponents>
+# @MasonParameters is the list of parameters for the constructor of
+# HTML::Mason's Apache or CGI Handler. This is normally only useful
+# for debugging, eg. profiling individual components with:
+# use MasonX::Profiler; # available on CPAN
+# @MasonParameters = (preamble => 'my $p = MasonX::Profiler->new($m, $r);');
-C<$HomepageComponents> is an arrayref of allowed components on a user's
-customized homepage ("RT at a glance").
-
-=cut
-
-Set($HomepageComponents, [qw(QuickCreate Quicksearch MyAdminQueues MySupportQueues MyReminders RefreshHomepage Dashboards)]);
-
-=item C<@MasonParameters>
-
-C<@MasonParameters> is the list of parameters for the constructor of
-HTML::Mason's Apache or CGI Handler. This is normally only useful
-for debugging, eg. profiling individual components with:
-
- use MasonX::Profiler; # available on CPAN
- Set(@MasonParameters, (preamble => 'my $p = MasonX::Profiler->new($m, $r);'));
-
-=cut
-
-Set(@MasonParameters, ());
-
-=item C<$DefaultSearchResultFormat>
-
-C<$DefaultSearchResultFormat> is the default format for RT search results
-
-=cut
+@MasonParameters = () unless (@MasonParameters);
+# $DefaultSearchResultFormat is the default format for RT search results
Set ($DefaultSearchResultFormat, qq{
- '<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__id__</a></B>/TITLE:#',
- '<B><A HREF="__WebPath__/Ticket/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
+ '<B><A HREF="$RT::WebPath/Ticket/Display.html?id=__id__">__id__</a></B>/TITLE:#',
+ '<B><A HREF="$RT::WebPath/Ticket/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
Status,
QueueName,
OwnerName,
@@ -1237,387 +507,105 @@ Set ($DefaultSearchResultFormat, qq{
'<small>__LastUpdatedRelative__</small>',
'<small>__TimeLeft__</small>'});
-=item C<$DefaultSelfServiceSearchResultFormat>
-
-C<$DefaultSelfServiceSearchResultFormat> is the default format of searches displayed in the
-SelfService interface.
-
-=cut
-
-Set($DefaultSelfServiceSearchResultFormat, qq{
- '<B><A HREF="__WebPath__/SelfService/Display.html?id=__id__">__id__</a></B>/TITLE:#',
- '<B><A HREF="__WebPath__/SelfService/Display.html?id=__id__">__Subject__</a></B>/TITLE:Subject',
- Status,
- Requestors,
- OwnerName});
-
-=item C<$SuppressInlineTextFiles>
-
-If C<$SuppressInlineTextFiles> is set to a true value, then uploaded
-text files (text-type attachments with file names) are prevented
-from being displayed in-line when viewing a ticket's history.
-
-=cut
+# If $SuppressInlineTextFiles is set to a true value, then uploaded
+# text files (text-type attachments with file names) are prevented
+# from being displayed in-line when viewing a ticket's history.
Set($SuppressInlineTextFiles, undef);
-=item C<$DontSearchFileAttachments>
-
-If C<$DontSearchFileAttachments> is set to a true value, then uploaded
-files (attachments with file names) are not searched during full-content
-ticket searches.
-
-=cut
+# If $DontSearchFileAttachments is set to a true value, then uploaded
+# files (attachments with file names) are not searched during full-content
+# ticket searches.
Set($DontSearchFileAttachments, undef);
-=item C<$ChartFont>
-
-The L<GD> module (which RT uses for graphs) uses a builtin font that doesn't
-have full Unicode support. You can use a particular TrueType font by setting
-$ChartFont to the absolute path of that font. Your GD library must have
-support for TrueType fonts to use this option.
-
-=cut
+# The GD module (which RT uses for graphs) uses a builtin font that doesn't
+# have full Unicode support. You can use a particular TrueType font by setting
+# $ChartFont to the absolute path of that font. Your GD library must have
+# support for TrueType fonts to use this option.
Set($ChartFont, undef);
-=item C<@Active_MakeClicky>
-
-MakeClicky detects various formats of data in headers and email
-messages, and extends them with supporting links. By default, RT
-provides two formats:
-
-* 'httpurl': detects http:// and https:// URLs and adds '[Open URL]'
- link after the URL.
-
-* 'httpurl_overwrite': also detects URLs as 'httpurl' format, but
- replace URL with link and *adds spaces* into text if it's longer
- then 30 chars. This allow browser to wrap long URLs and avoid
- horizontal scrolling.
-
-See F<share/html/Elements/MakeClicky> for documentation on how to add your own.
-
-=cut
-
-Set(@Active_MakeClicky, qw());
-
-=item C<$DefaultQueue>
-
-Use this to select the default queue name that will be used for creating new
-tickets. You may use either the queue's name or its ID. This only affects the
-queue selection boxes on the web interface.
-
-=cut
-
-#Set($DefaultQueue, 'General');
-
-=item C<$DefaultTimeUnitsToHours>
-
-Use this to set the default units for time entry to hours instead of minutes.
-
-=cut
-
-Set($DefaultTimeUnitsToHours, 0);
-
-=back
-
-=head1 L<Net::Server> (rt-server) Configuration
-
-=over 4
-
-=item C<$StandaloneMinServers>, C<$StandaloneMaxServers>
-
-The absolute minimum and maximum number of servers that will be created to
-handle requests. Having multiple servers means that serving a slow page will
-affect other users less.
-
-=cut
-
-Set($StandaloneMinServers, 1);
-Set($StandaloneMaxServers, 1);
-
-=item C<$StandaloneMinSpareServers>, C<$StandaloneMaxSpareServers>
-
-These next two options can be used to scale up and down the number of servers
-to adjust to load. These two options will respect the C<$StandaloneMinServers
-> and C<$StandaloneMaxServers options>.
-
-=cut
-
-Set($StandaloneMinSpareServers, 0);
-Set($StandaloneMaxSpareServers, 0);
-
-=item C<$StandaloneMaxRequests>
-
-This sets the absolute maximum number of requests a single server will serve.
-Setting this would be useful if, for example, memory usage slowly crawls up
-every hit.
-
-=cut
-
-#Set($StandaloneMaxRequests, 50);
-
-=item C<%NetServerOptions>
-
-C<%NetServerOptions> is a hash of additional options to use for
-L<Net::Server/DEFAULT ARGUMENTS>. For example, you could set
-reverse_lookups to get the hostnames for all users with:
-
-C<Set(%NetServerOptions, (reverse_lookups => 1));>
-
-=cut
-
-Set(%NetServerOptions, ());
-
-=back
-
-
-=head1 UTF-8 Configuration
-
-=over 4
-
-=item C<@LexiconLanguages>
-
-An array that contains languages supported by RT's internationalization
-interface. Defaults to all *.po lexicons; setting it to C<qw(en ja)> will make
-RT bilingual instead of multilingual, but will save some memory.
+# }}}
-=cut
-
-Set(@LexiconLanguages, qw(*));
-
-=item C<@EmailInputEncodings>
-
-An array that contains default encodings used to guess which charset
-an attachment uses if not specified. Must be recognized by
-L<Encode::Guess>.
-
-=cut
-
-Set(@EmailInputEncodings, qw(utf-8 iso-8859-1 us-ascii));
-
-=item C<$EmailOutputEncoding>
+# {{{ RT UTF-8 Settings
-The charset for localized email. Must be recognized by Encode.
-
-=cut
+# An array that contains languages supported by RT's internationalization
+# interface. Defaults to all *.po lexicons; setting it to qw(en ja) will make
+# RT bilingual instead of multilingual, but will save some memory.
-Set($EmailOutputEncoding, 'utf-8');
+@LexiconLanguages = qw(*) unless (@LexiconLanguages);
+# An array that contains default encodings used to guess which charset
+# an attachment uses if not specified. Must be recognized by
+# Encode::Guess.
-=back
+@EmailInputEncodings = qw(utf-8 iso-8859-1 us-ascii) unless (@EmailInputEncodings);
-=head1 Date Handling Configuration
+# The charset for localized email. Must be recognized by Encode.
-=over 4
+Set($EmailOutputEncoding , 'utf-8');
-=item C<$DateTimeFormat>
+# }}}
-You can choose date and time format. See "Output formatters"
-section in perldoc F<lib/RT/Date.pm> for more options. This option can
-be overridden by users in their preferences.
-Some examples:
+# {{{ RT Date Handling Options (for Time::ParseDate)
-C<Set($DateTimeFormat, 'LocalizedDateTime');>
-C<Set($DateTimeFormat, { Format => 'ISO', Seconds => 0 });>
-C<Set($DateTimeFormat, 'RFC2822');>
-C<Set($DateTimeFormat, { Format => 'RFC2822', Seconds => 0, DayOfWeek => 0 });>
-
-=cut
-
-Set($DateTimeFormat, 'DefaultFormat');
-
-# Next two options are for Time::ParseDate
-
-=item C<$DateDayBeforeMonth>
-
-Set this to 1 if your local date convention looks like "dd/mm/yy" instead of
-"mm/dd/yy". Used only for parsing, not for displaying dates.
-
-=cut
+# Set this to 1 if your local date convention looks like "dd/mm/yy"
+# instead of "mm/dd/yy".
Set($DateDayBeforeMonth , 1);
-=item C<$AmbiguousDayInPast>, C<$AmbiguousDayInFuture>
+# Should an unspecified day or year in a date refer to a future or a
+# past value? For example, should a date of "Tuesday" default to mean
+# the date for next Tuesday or last Tuesday? Should the date "March 1"
+# default to the date for next March or last March?
+# Set to 0 for the next date or 1 for the last date.
-Should an unspecified day or year in a date refer to a future or a
-past value? For example, should a date of "Tuesday" default to mean
-the date for next Tuesday or last Tuesday? Should the date "March 1"
-default to the date for next March or last March?
+Set($AmbiguousDayInPast , 1);
-Set $<AmbiguousDayInPast> for the last date, or $<$AmbiguousDayInFuture> for the
-next date.
+# }}}
-The default is usually good.
+# {{{ Miscellaneous RT Settings
-=cut
+# You can define new statuses and even reorder existing statuses here.
+# WARNING. DO NOT DELETE ANY OF THE DEFAULT STATUSES. If you do, RT
+# will break horribly. The statuses you add must be no longer than
+# 10 characters.
-Set($AmbiguousDayInPast, 0);
-Set($AmbiguousDayInFuture, 0);
+@ActiveStatus = qw(new open stalled) unless @ActiveStatus;
+@InactiveStatus = qw(resolved rejected deleted) unless @InactiveStatus;
-=back
-
-=head1 Approval Configuration
-
-Configration for the approvl system
-
-=over 4
-
-=item C<$ApprovalRejectionNotes>
-
-Should rejection notes be sent to the requestors? The default is true.
-
-=cut
-
-Set($ApprovalRejectionNotes, 1);
-
-
-=back
-
-=head1 Miscellaneous Configuration
-
-=over 4
-
-=item C<@ActiveStatus>, C<@InactiveStatus>
-
-You can define new statuses and even reorder existing statuses here.
-WARNING. DO NOT DELETE ANY OF THE DEFAULT STATUSES. If you do, RT
-will break horribly. The statuses you add must be no longer than
-10 characters.
-
-=cut
-
-Set(@ActiveStatus, qw(new open stalled));
-Set(@InactiveStatus, qw(resolved rejected deleted));
-
-=item C<$LinkTransactionsRun1Scrip>
-
-RT-3.4 backward compatibility setting. Add/Delete Link used to record one
-transaction and run one scrip. Set this value to 1 if you want
-only one of the link transactions to have scrips run.
-
-=cut
-
-Set($LinkTransactionsRun1Scrip, 0);
-
-=item C<$StrictLinkACL>
-
-When this feature is enabled a user needs I<ModifyTicket> rights on both
-tickets to link them together, otherwise he can have rights on either of
-them.
-
-=cut
+# Backward compatability setting. Add/Delete Link used to record one
+# transaction and run one scrip. Set this value to 1 if you want
+# only one of the link transactions to have scrips run.
+Set($LinkTransactionsRun1Scrip , 0);
+# When this feature is enabled an user need ModifyTicket right on both
+# tickets to link them together, otherwise he can have right on any of
+# two.
Set($StrictLinkACL, 1);
-=item C<$PreviewScripMessages>
-
-Set C<$PreviewScripMessages> to 1 if the scrips preview on the ticket
-reply page should include the content of the messages to be sent.
-
-=cut
+# }}}
-Set($PreviewScripMessages, 0);
-
-=item C<$UseTransactionBatch>
-
-Set C<$UseTransactionBatch> to 1 to execute transactions in batches,
-such that a resolve and comment (for example) would happen
-simultaneously, instead of as two transactions, unaware of each
-others' existence.
-
-=cut
-
-Set($UseTransactionBatch, 1);
-
-=item C<@CustomFieldValuesSources>
-
-Set C<@CustomFieldValuesSources> to a list of class names which extend
-L<RT::CustomFieldValues::External>. This can be used to pull lists of
-custom field values from external sources at runtime.
-
-=cut
-Set(@CustomFieldValuesSources, ());
-
-=item C<$CanonicalizeRedirectURLs>
-
-Set C<$CanonicalizeRedirectURLs> to 1 to use $C<WebURL> when redirecting rather
-than the one we get from C<%ENV>.
-
-If you use RT behind a reverse proxy, you almost certainly want to
-enable this option.
-
-=cut
-
-Set($CanonicalizeRedirectURLs, 0);
-=item C<$EnableReminders>
-
-Hide links/portlets related to Reminders by setting this to 0
-
-=cut
-
-Set($EnableReminders,1);
-
-
-=item C<@Plugins>
-
-Set C<@Plugins> to a list of external RT plugins that should be enabled (those
-plugins have to be previously downloaded and installed).
-Example:
-
-C<Set(@Plugins, (qw(Extension::QuickDelete RT::FM)));>
-
-=cut
-
-Set(@Plugins, ());
-
-=back
-
-=head1 Development Configuration
-
-=over 4
-
-=item C<$DevelMode>
-
-RT comes with a "Development mode" setting.
-This setting, as a convenience for developers, turns on
-all sorts of development options that you most likely don't want in
-production:
-
-* Turns off Mason's 'static_source' directive. By default, you can't
- edit RT's web ui components on the fly and have RT magically pick up
- your changes. (It's a big performance hit)
-
- * More to come
-
-=cut
+# {{{ Development Mode
+#
+# RT comes with a "Development mode" setting.
+# This setting, as a convenience for developers, turns on
+# all sorts of development options that you most likely don't want in
+# production:
+#
+# * Turns off Mason's 'static_source' directive. By default, you can't
+# edit RT's web ui components on the fly and have RT magically pick up
+# your changes. (It's a big performance hit)
+#
+# * More to come
+#
Set($DevelMode, '@RT_DEVEL_MODE@');
+# }}}
-=back
-
-=head1 Deprecated Options
-
-=over 4
-
-=item C<$AlwaysUseBase64>
-
-Encode blobs as base64 in DB (?)
-
-=item C<$TicketBaseURI>
-
-Base URI to tickets in this system; used when loading (?)
-
-=item C<$UseCodeTickets>
-
-This option is exists for backwards compatibility. Don't use it.
-
-=back
-
-=cut
1;
diff --git a/rt/etc/RT_SiteConfig.pm b/rt/etc/RT_SiteConfig.pm
index 1661e4d6e..7a6f40735 100644
--- a/rt/etc/RT_SiteConfig.pm
+++ b/rt/etc/RT_SiteConfig.pm
@@ -14,6 +14,41 @@
#
# perl -c /path/to/your/etc/RT_SiteConfig.pm
-Set( $rtname, 'example.com');
-#Set(@Plugins,(qw(Extension::QuickDelete RT::FM)));
+#Set( $rtname, 'example.com');
+
+# These settings should have been inserted by the initial Freeside install.
+# Sometimes you may want to change domain, timezone, or freeside::URL later,
+# everything else should probably stay untouched.
+
+$RT::rtname = '%%%RT_DOMAIN%%%';
+$RT::Organization = '%%%RT_DOMAIN%%%';
+
+$RT::Timezone = '%%%RT_TIMEZONE%%%';
+
+$RT::WebExternalAuth = 1;
+$RT::WebFallbackToInternal = 1; #no
+$RT::WebExternalAuto = 1;
+
+$RT::URI::freeside::IntegrationType = 'Internal';
+$RT::URI::freeside::URL = '%%%FREESIDE_URL%%%';
+
+$RT::URI::freeside::URL =~ m(^(https?://[^/]+)(/.*)$)i;
+$RT::WebBaseURL = $1;
+$RT::WebPath = "$2/rt";
+
+Set($DatabaseHost , '');
+
+# These settings are user-editable.
+
+#old, RT 3.4 style (deprecated, useless):
+#$RT::MyTicketsLength = 10;
+#NEW, RT 3.6 style (uncomment to use):
+#Set($DefaultSummaryRows, 10);
+
+$RT::QuickCreateLong = 0; #set to true to cause quick ticket creation to
+ #redirect to the "long" ticket creation screen
+ #instead of just creating a ticket with the subject.
+
+Set($MessageBoxWidth , 80);
+
1;
diff --git a/rt/etc/acl.Oracle b/rt/etc/acl.Oracle
index 9ca4122a0..c8667c031 100644
--- a/rt/etc/acl.Oracle
+++ b/rt/etc/acl.Oracle
@@ -1,4 +1,10 @@
-
-sub acl { return () }
-
+sub acl {
+return (
+"CREATE USER ${RT::DatabaseUser} identified by ${RT::DatabasePassword}".
+"temporary tablespace TEMP" .
+"default tablespace USERS" .
+"quota unlimited on USERS;" ,
+"grant connect, resource to ${RT::DatabaseUser};",
+"exit;");
+}
1;
diff --git a/rt/etc/acl.Pg b/rt/etc/acl.Pg
index 8a0d4f28c..16ea71b2d 100755
--- a/rt/etc/acl.Pg
+++ b/rt/etc/acl.Pg
@@ -1,76 +1,63 @@
-
sub acl {
my $dbh = shift;
my @acls;
my @tables = qw (
- attachments_id_seq
- Attachments
- Attributes
- attributes_id_seq
- queues_id_seq
- Queues
- links_id_seq
- Links
- principals_id_seq
- Principals
- groups_id_seq
- Groups
- scripconditions_id_seq
- ScripConditions
- transactions_id_seq
- Transactions
- scrips_id_seq
- Scrips
- acl_id_seq
- ACL
- groupmembers_id_seq
- GroupMembers
- cachedgroupmembers_id_seq
- CachedGroupMembers
- users_id_seq
- Users
- tickets_id_seq
- Tickets
- scripactions_id_seq
- ScripActions
- templates_id_seq
- Templates
- objectcustomfieldvalues_id_s
- ObjectCustomFieldValues
- customfields_id_seq
- CustomFields
- objectcustomfields_id_s
- ObjectCustomFields
- customfieldvalues_id_seq
- CustomFieldValues
- sessions
- );
- my $db_user = RT->Config->Get('DatabaseUser');
- my $db_pass = RT->Config->Get('DatabasePassword');
+ attachments_id_seq
+ Attachments
+ queues_id_seq
+ Queues
+ links_id_seq
+ Links
+ principals_id_seq
+ Principals
+ groups_id_seq
+ Groups
+ scripconditions_id_seq
+ ScripConditions
+ transactions_id_seq
+ Transactions
+ scrips_id_seq
+ Scrips
+ acl_id_seq
+ ACL
+ groupmembers_id_seq
+ GroupMembers
+ cachedgroupmembers_id_seq
+ CachedGroupMembers
+ users_id_seq
+ Users
+ tickets_id_seq
+ Tickets
+ scripactions_id_seq
+ ScripActions
+ templates_id_seq
+ Templates
+ ticketcustomfieldvalues_id_s
+ TicketCustomFieldValues
+ customfields_id_seq
+ CustomFields
+ customfieldvalues_id_seq
+ CustomFieldValues
+ sessions
+ );
- # if there's already an rt_user, use it.
- my @row = $dbh->selectrow_array( "SELECT usename FROM pg_user WHERE usename = '$db_user'" );
- unless ( $row[0] ) {
- push @acls, "CREATE USER \"$db_user\" WITH PASSWORD '$db_pass' NOCREATEDB NOCREATEUSER;";
+ # if there's already an rt_user, drop it.
+ my @row =
+ $dbh->selectrow_array( "select usename from pg_user where usename = '" . $RT::DatabaseUser."'" );
+ if ( $row[0] ) {
+ push @acls, "drop user ${RT::DatabaseUser};",;
}
- my $sequence_right
- = ( $dbh->{pg_server_version} >= 80200 )
- ? "USAGE, SELECT, UPDATE"
- : "SELECT, UPDATE";
+ push @acls, "create user ${RT::DatabaseUser} with password '${RT::DatabasePassword}' NOCREATEDB NOCREATEUSER;";
foreach my $table (@tables) {
- if ( $table =~ /^[a-z]/ && $table ne 'sessions' ) {
-# table like objectcustomfields_id_s
- push @acls, "GRANT $sequence_right ON $table TO \"$db_user\";"
- }
- else {
- push @acls, "GRANT SELECT, INSERT, UPDATE, DELETE ON $table TO \"$db_user\";"
- }
+ push @acls,
+ "GRANT SELECT, INSERT, UPDATE, DELETE ON $table to "
+ . $RT::DatabaseUser . ";";
+
}
return (@acls);
}
-
1;
diff --git a/rt/etc/acl.mysql b/rt/etc/acl.mysql
index 0982ca228..0ecaa3b15 100755
--- a/rt/etc/acl.mysql
+++ b/rt/etc/acl.mysql
@@ -1,27 +1,8 @@
-
sub acl {
- my $db_name = RT->Config->Get('DatabaseName');
- my $db_rthost = RT->Config->Get('DatabaseRTHost');
- my $db_user = RT->Config->Get('DatabaseUser');
- my $db_pass = RT->Config->Get('DatabasePassword');
- unless ( $db_user ) {
- print STDERR "DatabaseUser option is not defined or empty. Skipping...\n";
- return;
- }
- if ( $db_user eq 'root' ) {
- print STDERR "DatabaseUser is root. Skipping...\n";
- return;
- }
- print "Granting access to $db_user\@'$db_rthost' on $db_name.\n";
- return (
- "USE mysql;",
- "DELETE FROM user WHERE user = '$db_user';",
- "DELETE FROM db where db = '$db_name';",
- "GRANT SELECT,INSERT,CREATE,INDEX,UPDATE,DELETE
- ON $db_name.*
- TO '$db_user'\@'$db_rthost'
- IDENTIFIED BY '$db_pass';",
- );
+return (
+"USE mysql;",
+"DELETE FROM user WHERE user = '${RT::DatabaseUser}';",
+"DELETE FROM db where db = '${RT::DatabaseName}';",
+"GRANT SELECT,INSERT,CREATE,INDEX,UPDATE,DELETE ON ${RT::DatabaseName}.* TO ${RT::DatabaseUser}\@${RT::DatabaseRTHost} IDENTIFIED BY '${RT::DatabasePassword}';");
}
-
1;
diff --git a/rt/etc/schema.Oracle b/rt/etc/schema.Oracle
deleted file mode 100644
index 693e75ae5..000000000
--- a/rt/etc/schema.Oracle
+++ /dev/null
@@ -1,399 +0,0 @@
-
-CREATE SEQUENCE ATTACHMENTS_seq;
-CREATE TABLE Attachments (
- id NUMBER(11,0)
- CONSTRAINT Attachments_Key PRIMARY KEY,
- TransactionId NUMBER(11,0) NOT NULL,
- Parent NUMBER(11,0) DEFAULT 0 NOT NULL,
- MessageId VARCHAR2(160),
- Subject VARCHAR2(255),
- Filename VARCHAR2(255),
- ContentType VARCHAR2(80),
- ContentEncoding VARCHAR2(80),
- Content CLOB,
- Headers CLOB,
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE
-);
-CREATE INDEX Attachments2 ON Attachments (TransactionId);
-CREATE INDEX Attachments3 ON Attachments (Parent, TransactionId);
-
-
-CREATE SEQUENCE QUEUES_seq;
-CREATE TABLE Queues (
- id NUMBER(11,0)
- CONSTRAINT Queues_Key PRIMARY KEY,
- Name VARCHAR2(200) CONSTRAINT Queues_Name_Unique UNIQUE NOT NULL,
- Description VARCHAR2(255),
- CorrespondAddress VARCHAR2(120),
- CommentAddress VARCHAR2(120),
- InitialPriority NUMBER(11,0) DEFAULT 0 NOT NULL,
- FinalPriority NUMBER(11,0) DEFAULT 0 NOT NULL,
- DefaultDueIn NUMBER(11,0) DEFAULT 0 NOT NULL,
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE,
- Disabled NUMBER(11,0) DEFAULT 0 NOT NULL
-);
- CREATE INDEX Queues1 ON Queues (LOWER(Name));
-CREATE INDEX Queues2 ON Queues (Disabled);
-
-
-CREATE SEQUENCE LINKS_seq;
-CREATE TABLE Links (
- id NUMBER(11,0)
- CONSTRAINT Links_Key PRIMARY KEY,
- Base VARCHAR2(240),
- Target VARCHAR2(240),
- Type VARCHAR2(20) NOT NULL,
- LocalTarget NUMBER(11,0) DEFAULT 0 NOT NULL,
- LocalBase NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE,
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE
-);
-CREATE UNIQUE INDEX Links1 ON Links (Base, Target, Type);
-CREATE INDEX Links2 ON Links (Base, Type);
-CREATE INDEX Links3 ON Links (Target, Type);
-CREATE INDEX Links4 ON Links(Type,LocalBase);
-
-
-CREATE SEQUENCE PRINCIPALS_seq;
-CREATE TABLE Principals (
- id NUMBER(11,0)
- CONSTRAINT Principals_Key PRIMARY KEY,
- PrincipalType VARCHAR2(16),
- ObjectId NUMBER(11,0),
- Disabled NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-CREATE UNIQUE INDEX Principals2 ON Principals (ObjectId);
-
-
-CREATE SEQUENCE GROUPS_seq;
-CREATE TABLE Groups (
- id NUMBER(11,0)
- CONSTRAINT Groups_Key PRIMARY KEY,
- Name VARCHAR2(200),
- Description VARCHAR2(255),
- Domain VARCHAR2(64),
- Type VARCHAR2(64),
- Instance NUMBER(11,0) DEFAULT 0 -- NOT NULL
--- Instance VARCHAR2(64)
-);
-CREATE INDEX Groups1 ON Groups (LOWER(Domain), Instance, LOWER(Type), id);
-CREATE INDEX Groups2 ON Groups (LOWER(Type), Instance, LOWER(Domain));
-
-
-CREATE SEQUENCE SCRIPCONDITIONS_seq;
-CREATE TABLE ScripConditions (
- id NUMBER(11, 0)
- CONSTRAINT ScripConditions_Key PRIMARY KEY,
- Name VARCHAR2(200),
- Description VARCHAR2(255),
- ExecModule VARCHAR2(60),
- Argument VARCHAR2(255),
- ApplicableTransTypes VARCHAR2(60),
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE
-);
-
-
-CREATE SEQUENCE TRANSACTIONS_seq;
-CREATE TABLE Transactions (
- id NUMBER(11,0)
- CONSTRAINT Transactions_Key PRIMARY KEY,
- ObjectType VARCHAR2(255),
- ObjectId NUMBER(11,0) DEFAULT 0 NOT NULL,
- TimeTaken NUMBER(11,0) DEFAULT 0 NOT NULL,
- Type VARCHAR2(20),
- Field VARCHAR2(40),
- OldValue VARCHAR2(255),
- NewValue VARCHAR2(255),
- ReferenceType VARCHAR2(255),
- OldReference NUMBER(11,0),
- NewReference NUMBER(11,0),
- Data VARCHAR2(255),
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE
-);
-CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
-
-
-CREATE SEQUENCE SCRIPS_seq;
-CREATE TABLE Scrips (
- id NUMBER(11,0)
- CONSTRAINT Scrips_Key PRIMARY KEY,
- Description VARCHAR2(255),
- ScripCondition NUMBER(11,0) DEFAULT 0 NOT NULL,
- ScripAction NUMBER(11,0) DEFAULT 0 NOT NULL,
- ConditionRules CLOB,
- ActionRules CLOB,
- CustomIsApplicableCode CLOB,
- CustomPrepareCode CLOB,
- CustomCommitCode CLOB,
- Stage VARCHAR2(32),
- Queue NUMBER(11,0) DEFAULT 0 NOT NULL,
- Template NUMBER(11,0) DEFAULT 0 NOT NULL,
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE
-);
-
-
-CREATE SEQUENCE ACL_seq;
-CREATE TABLE ACL (
- id NUMBER(11,0)
- CONSTRAINT ACL_Key PRIMARY KEY,
- PrincipalType VARCHAR2(25) NOT NULL,
- PrincipalId NUMBER(11,0) NOT NULL,
- RightName VARCHAR2(25) NOT NULL,
- ObjectType VARCHAR2(25) NOT NULL,
- ObjectId NUMBER(11,0) DEFAULT 0 NOT NULL,
- DelegatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- DelegatedFrom NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-CREATE INDEX ACL1 ON ACL(RightName, ObjectType, ObjectId, PrincipalType, PrincipalId);
-
-
-CREATE SEQUENCE GROUPMEMBERS_seq;
-CREATE TABLE GroupMembers (
- id NUMBER(11,0)
- CONSTRAINT GroupMembers_Key PRIMARY KEY,
- GroupId NUMBER(11,0) DEFAULT 0 NOT NULL,
- MemberId NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-CREATE UNIQUE INDEX GroupMembers1 ON GroupMembers (GroupId, MemberId);
-
-
-CREATE SEQUENCE CachedGroupMembers_seq;
-CREATE TABLE CachedGroupMembers (
- id NUMBER(11,0)
- CONSTRAINT CachedGroupMembers_Key PRIMARY KEY,
- GroupId NUMBER(11,0),
- MemberId NUMBER(11,0),
- Via NUMBER(11,0),
- ImmediateParentId NUMBER(11,0),
- Disabled NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-CREATE INDEX DisGrouMem ON CachedGroupMembers (GroupId, MemberId, Disabled);
-CREATE INDEX GrouMem ON CachedGroupMembers (GroupId, MemberId);
-CREATE INDEX CachedGroupMembers3 on CachedGroupMembers (MemberId, ImmediateParentId);
-
-
-CREATE SEQUENCE USERS_seq;
-CREATE TABLE Users (
- id NUMBER(11,0)
- CONSTRAINT Users_Key PRIMARY KEY,
- Name VARCHAR2(200) CONSTRAINT Users_Name_Unique
- unique NOT NULL,
- Password VARCHAR2(40),
- Comments CLOB,
- Signature CLOB,
- EmailAddress VARCHAR2(120),
- FreeFormContactInfo CLOB,
- Organization VARCHAR2(200),
- RealName VARCHAR2(120),
- NickName VARCHAR2(16),
- Lang VARCHAR2(16),
- EmailEncoding VARCHAR2(16),
- WebEncoding VARCHAR2(16),
- ExternalContactInfoId VARCHAR2(100),
- ContactInfoSystem VARCHAR2(30),
- ExternalAuthId VARCHAR2(100),
- AuthSystem VARCHAR2(30),
- Gecos VARCHAR2(16),
- HomePhone VARCHAR2(30),
- WorkPhone VARCHAR2(30),
- MobilePhone VARCHAR2(30),
- PagerPhone VARCHAR2(30),
- Address1 VARCHAR2(200),
- Address2 VARCHAR2(200),
- City VARCHAR2(100),
- State VARCHAR2(100),
- Zip VARCHAR2(16),
- Country VARCHAR2(50),
- Timezone VARCHAR2(50),
- PGPKey CLOB,
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE
-);
--- CREATE UNIQUE INDEX Users1 ON Users (Name);
-
-CREATE INDEX Users2 ON Users( LOWER(Name));
-CREATE INDEX Users4 ON Users (LOWER(EmailAddress));
-
-
-CREATE SEQUENCE TICKETS_seq;
-CREATE TABLE Tickets (
- id NUMBER(11, 0)
- CONSTRAINT Tickets_Key PRIMARY KEY,
- EffectiveId NUMBER(11,0) DEFAULT 0 NOT NULL,
- Queue NUMBER(11,0) DEFAULT 0 NOT NULL,
- Type VARCHAR2(16),
- IssueStatement NUMBER(11,0) DEFAULT 0 NOT NULL,
- Resolution NUMBER(11,0) DEFAULT 0 NOT NULL,
- Owner NUMBER(11,0) DEFAULT 0 NOT NULL,
- Subject VARCHAR2(200) DEFAULT '[no subject]',
- InitialPriority NUMBER(11,0) DEFAULT 0 NOT NULL,
- FinalPriority NUMBER(11,0) DEFAULT 0 NOT NULL,
- Priority NUMBER(11,0) DEFAULT 0 NOT NULL,
- TimeEstimated NUMBER(11,0) DEFAULT 0 NOT NULL,
- TimeWorked NUMBER(11,0) DEFAULT 0 NOT NULL,
- Status VARCHAR2(10),
- TimeLeft NUMBER(11,0) DEFAULT 0 NOT NULL,
- Told DATE,
- Starts DATE,
- Started DATE,
- Due DATE,
- Resolved DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE,
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE,
- Disabled NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-CREATE INDEX Tickets1 ON Tickets (Queue, Status);
-CREATE INDEX Tickets2 ON Tickets (Owner);
-CREATE INDEX Tickets4 ON Tickets (id, Status);
-CREATE INDEX Tickets5 ON Tickets (id, EffectiveId);
-CREATE INDEX Tickets6 ON Tickets (EffectiveId, Type);
-
-
-CREATE SEQUENCE SCRIPACTIONS_seq;
-CREATE TABLE ScripActions (
- id NUMBER(11,0)
- CONSTRAINT ScripActions_Key PRIMARY KEY,
- Name VARCHAR2(200),
- Description VARCHAR2(255),
- ExecModule VARCHAR2(60),
- Argument VARCHAR2(255),
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE
-);
-
-
-CREATE SEQUENCE TEMPLATES_seq;
-CREATE TABLE Templates (
- id NUMBER(11,0)
- CONSTRAINT Templates_Key PRIMARY KEY,
- Queue NUMBER(11,0) DEFAULT 0 NOT NULL,
- Name VARCHAR2(200) NOT NULL,
- Description VARCHAR2(255),
- Type VARCHAR2(16),
- Language VARCHAR2(16),
- TranslationOf NUMBER(11,0) DEFAULT 0 NOT NULL,
- Content CLOB,
- LastUpdated DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE
-);
-
-
-CREATE SEQUENCE OBJECTCUSTOMFIELDS_seq;
-CREATE TABLE ObjectCustomFields (
- id NUMBER(11,0)
- CONSTRAINT ObjectCustomFields_Key PRIMARY KEY,
- CustomField NUMBER(11,0) NOT NULL,
- ObjectId NUMBER(11,0) NOT NULL,
- SortOrder NUMBER(11,0) DEFAULT 0 NOT NULL,
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE
-);
-
-
-CREATE SEQUENCE OBJECTCUSTOMFIELDVALUES_seq;
-CREATE TABLE ObjectCustomFieldValues (
- id NUMBER(11,0)
- CONSTRAINT ObjectCustomFieldValues_Key PRIMARY KEY,
- CustomField NUMBER(11,0) NOT NULL,
- ObjectType VARCHAR2(25) NOT NULL,
- ObjectId NUMBER(11,0) DEFAULT 0 NOT NULL,
- SortOrder NUMBER(11,0) DEFAULT 0 NOT NULL,
- Content VARCHAR2(255),
- LargeContent CLOB,
- ContentType VARCHAR2(80),
- ContentEncoding VARCHAR2(80),
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE,
- Disabled NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-
-CREATE INDEX ObjectCustomFieldValues1 ON ObjectCustomFieldValues (Content);
-CREATE INDEX ObjectCustomFieldValues2 ON ObjectCustomFieldValues (CustomField,ObjectType,ObjectId);
-
-CREATE SEQUENCE CUSTOMFIELDS_seq;
-CREATE TABLE CustomFields (
- id NUMBER(11,0)
- CONSTRAINT CustomFields_Key PRIMARY KEY,
- Name VARCHAR2(200),
- Type VARCHAR2(200),
- MaxValues NUMBER(11,0) DEFAULT 0 NOT NULL,
- Pattern CLOB,
- Repeated NUMBER(11,0) DEFAULT 0 NOT NULL,
- Description VARCHAR2(255),
- SortOrder NUMBER(11,0) DEFAULT 0 NOT NULL,
- LookupType VARCHAR2(255),
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE,
- Disabled NUMBER(11,0) DEFAULT 0 NOT NULL
-);
-
-
-CREATE SEQUENCE CUSTOMFIELDVALUES_seq;
-CREATE TABLE CustomFieldValues (
- id NUMBER(11,0)
- CONSTRAINT CustomFieldValues_Key PRIMARY KEY,
- CustomField NUMBER(11,0),
- Name VARCHAR2(200),
- Description VARCHAR2(255),
- SortOrder NUMBER(11,0) DEFAULT 0 NOT NULL,
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE
-);
-
-CREATE INDEX CustomFieldValues1 ON CustomFieldValues (CustomField);
-
-CREATE SEQUENCE ATTRIBUTES_seq;
-CREATE TABLE Attributes (
- id NUMBER(11,0) PRIMARY KEY,
- Name VARCHAR2(255) NOT NULL,
- Description VARCHAR2(255),
- Content CLOB,
- ContentType VARCHAR(16),
- ObjectType VARCHAR2(25) NOT NULL,
- ObjectId NUMBER(11,0) DEFAULT 0 NOT NULL,
- Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
- Created DATE,
- LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
- LastUpdated DATE
-);
-
-CREATE INDEX Attributes1 on Attributes(Name);
-CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
-
-
-CREATE TABLE sessions (
- id VARCHAR2(32)
- CONSTRAINT Sessions_Key PRIMARY KEY,
- a_session CLOB,
- LastUpdated DATE
-);
-
diff --git a/rt/etc/schema.Pg b/rt/etc/schema.Pg
index 48525c8d7..2d45a946a 100755
--- a/rt/etc/schema.Pg
+++ b/rt/etc/schema.Pg
@@ -283,8 +283,6 @@ CREATE TABLE GroupMembers (
);
-CREATE UNIQUE INDEX GroupMembers1 ON GroupMembers(GroupId, MemberId);
-
-- }}}
-- {{{ GroupMembersCache
@@ -366,6 +364,7 @@ CREATE TABLE Users (
CREATE UNIQUE INDEX Users1 ON Users (Name) ;
+CREATE INDEX Users2 ON Users (Name);
CREATE INDEX Users3 ON Users (id, EmailAddress);
CREATE INDEX Users4 ON Users (EmailAddress);
@@ -529,7 +528,7 @@ CREATE TABLE CustomFields (
Type varchar(200) NULL ,
MaxValues integer NOT NULL DEFAULT 0 ,
Repeated integer NOT NULL DEFAULT 0 ,
- Pattern varchar(65536) NULL ,
+ Pattern varchar(255) NULL ,
LookupType varchar(255) NOT NULL ,
Description varchar(255) NULL ,
SortOrder integer NOT NULL DEFAULT 0 ,
diff --git a/rt/etc/schema.mysql b/rt/etc/schema.mysql
index b7d53f884..46f8ec562 100755
--- a/rt/etc/schema.mysql
+++ b/rt/etc/schema.mysql
@@ -16,6 +16,7 @@ CREATE TABLE Attachments (
PRIMARY KEY (id)
) TYPE=InnoDB;
+CREATE INDEX Attachments1 ON Attachments (Parent) ;
CREATE INDEX Attachments2 ON Attachments (TransactionId) ;
CREATE INDEX Attachments3 ON Attachments (Parent, TransactionId) ;
# }}}
@@ -58,9 +59,9 @@ CREATE TABLE Links (
PRIMARY KEY (id)
) TYPE=InnoDB;
+CREATE UNIQUE INDEX Links1 ON Links (Base, Target, Type) ;
CREATE INDEX Links2 ON Links (Base, Type) ;
CREATE INDEX Links3 ON Links (Target, Type) ;
-CREATE INDEX Links4 ON Links (Type,LocalBase);
# }}}
@@ -86,12 +87,12 @@ CREATE TABLE Groups (
Description varchar(255) NULL ,
Domain varchar(64),
Type varchar(64),
- Instance integer,
+ Instance varchar(64),
PRIMARY KEY (id)
) TYPE=InnoDB;
CREATE INDEX Groups1 ON Groups (Domain,Instance,Type,id);
-CREATE INDEX Groups2 On Groups (Type, Instance);
+CREATE INDEX Groups2 On Groups (Type, Instance, Domain);
# }}}
@@ -117,23 +118,21 @@ CREATE TABLE ScripConditions (
# {{{ Transactions
CREATE TABLE Transactions (
id INTEGER NOT NULL AUTO_INCREMENT,
- ObjectType varchar(64) NOT NULL,
- ObjectId integer NOT NULL DEFAULT 0 ,
+ EffectiveTicket integer NOT NULL DEFAULT 0 ,
+ Ticket integer NOT NULL DEFAULT 0 ,
TimeTaken integer NOT NULL DEFAULT 0 ,
Type varchar(20) NULL ,
Field varchar(40) NULL ,
OldValue varchar(255) NULL ,
NewValue varchar(255) NULL ,
- ReferenceType varchar(255) NULL,
- OldReference integer NULL ,
- NewReference integer NULL ,
- Data varchar(255) NULL ,
+ Data varchar(100) NULL ,
Creator integer NOT NULL DEFAULT 0 ,
Created DATETIME NULL ,
PRIMARY KEY (id)
) TYPE=InnoDB;
-CREATE INDEX Transactions1 ON Transactions (ObjectType, ObjectId);
+CREATE INDEX Transactions1 ON Transactions (Ticket);
+CREATE INDEX Transactions2 ON Transactions (EffectiveTicket);
# }}}
@@ -211,6 +210,7 @@ create table CachedGroupMembers (
) TYPE=InnoDB;
CREATE INDEX DisGrouMem on CachedGroupMembers (GroupId,MemberId,Disabled);
+CREATE INDEX GrouMem on CachedGroupMembers (GroupId,MemberId);
# }}}
@@ -257,6 +257,8 @@ CREATE TABLE Users (
CREATE UNIQUE INDEX Users1 ON Users (Name) ;
+CREATE INDEX Users2 ON Users (Name);
+CREATE INDEX Users3 ON Users (id, EmailAddress);
CREATE INDEX Users4 ON Users (EmailAddress);
@@ -297,6 +299,9 @@ CREATE TABLE Tickets (
CREATE INDEX Tickets1 ON Tickets (Queue, Status) ;
CREATE INDEX Tickets2 ON Tickets (Owner) ;
+CREATE INDEX Tickets3 ON Tickets (EffectiveId) ;
+CREATE INDEX Tickets4 ON Tickets (id, Status) ;
+CREATE INDEX Tickets5 ON Tickets (id, EffectiveId) ;
CREATE INDEX Tickets6 ON Tickets (EffectiveId, Type) ;
# }}}
@@ -338,31 +343,21 @@ CREATE TABLE Templates (
# }}}
-# {{{ ObjectCustomFieldValues
+# {{{ TicketCustomFieldValues
-CREATE TABLE ObjectCustomFieldValues (
+CREATE TABLE TicketCustomFieldValues (
id INTEGER NOT NULL AUTO_INCREMENT,
+ Ticket int NOT NULL ,
CustomField int NOT NULL ,
- ObjectType varchar(255) NOT NULL, # Final target of the Object
- ObjectId int NOT NULL , # New -- Replaces Ticket
- SortOrder integer NOT NULL DEFAULT 0 , # New -- ordering for multiple values
-
Content varchar(255) NULL ,
- LargeContent LONGTEXT NULL, # New -- to hold 255+ strings
- ContentType varchar(80) NULL, # New -- only text/* gets searched
- ContentEncoding varchar(80) NULL , # New -- for binary Content
Creator integer NOT NULL DEFAULT 0 ,
Created DATETIME NULL ,
LastUpdatedBy integer NOT NULL DEFAULT 0 ,
LastUpdated DATETIME NULL ,
- Disabled int2 NOT NULL DEFAULT 0 , # New -- whether the value was current
PRIMARY KEY (id)
) TYPE=InnoDB;
-CREATE INDEX ObjectCustomFieldValues1 ON ObjectCustomFieldValues (Content);
-CREATE INDEX ObjectCustomFieldValues2 ON ObjectCustomFieldValues (CustomField,ObjectType,ObjectId);
-
# }}}
# {{{ CustomFields
@@ -370,13 +365,10 @@ CREATE INDEX ObjectCustomFieldValues2 ON ObjectCustomFieldValues (CustomField,Ob
CREATE TABLE CustomFields (
id INTEGER NOT NULL AUTO_INCREMENT,
Name varchar(200) NULL ,
- Type varchar(200) NULL , # Changed -- 'Single' and 'Multiple' is moved out
- MaxValues integer, # New -- was 'Single'(1) and 'Multiple'(0)
- Pattern varchar(255) NULL , # New -- Must validate against this
- Repeated int2 NOT NULL DEFAULT 0 , # New -- repeated table entry
+ Type varchar(200) NULL ,
+ Queue integer NOT NULL DEFAULT 0 ,
Description varchar(255) NULL ,
SortOrder integer NOT NULL DEFAULT 0 ,
- LookupType varchar(255) NOT NULL,
Creator integer NOT NULL DEFAULT 0 ,
Created DATETIME NULL ,
@@ -386,22 +378,8 @@ CREATE TABLE CustomFields (
PRIMARY KEY (id)
) TYPE=InnoDB;
-# }}}
-
-# {{{ ObjectCustomFields
+CREATE INDEX CustomFields1 on CustomFields (Disabled, Queue);
-CREATE TABLE ObjectCustomFields (
- id INTEGER NOT NULL AUTO_INCREMENT,
- CustomField int NOT NULL ,
- ObjectId integer NOT NULL,
- SortOrder integer NOT NULL DEFAULT 0 ,
-
- Creator integer NOT NULL DEFAULT 0 ,
- Created DATETIME NULL ,
- LastUpdatedBy integer NOT NULL DEFAULT 0 ,
- LastUpdated DATETIME NULL ,
- PRIMARY KEY (id)
-) TYPE=InnoDB;
# }}}
@@ -421,31 +399,6 @@ CREATE TABLE CustomFieldValues (
PRIMARY KEY (id)
) TYPE=InnoDB;
-CREATE INDEX CustomFieldValues1 ON CustomFieldValues (CustomField);
-
-# }}}
-
-
-# {{{ Attributes
-
-CREATE TABLE Attributes (
- id INTEGER NOT NULL AUTO_INCREMENT,
- Name varchar(255) NULL ,
- Description varchar(255) NULL ,
- Content text,
- ContentType varchar(16),
- ObjectType varchar(64),
- ObjectId integer, # foreign key to anything
- Creator integer NOT NULL DEFAULT 0 ,
- Created DATETIME NULL ,
- LastUpdatedBy integer NOT NULL DEFAULT 0 ,
- LastUpdated DATETIME NULL ,
- PRIMARY KEY (id)
-) TYPE=InnoDB;
-
-CREATE INDEX Attributes1 on Attributes(Name);
-CREATE INDEX Attributes2 on Attributes(ObjectType, ObjectId);
-
# }}}
# {{{ Sessions
diff --git a/rt/etc/upgrade/2.1.71 b/rt/etc/upgrade/2.1.71
deleted file mode 100644
index cb89a3ac3..000000000
--- a/rt/etc/upgrade/2.1.71
+++ /dev/null
@@ -1,211 +0,0 @@
-@Queues = ( {
- Name => '___Approvals',
- Description => 'A system-internal queue for the approvals system',
- Disabled => 2,
- }
-);
-
-
-
-
-
-# {{{ Templates
-@Templates = (
- {
- Queue => '___Approvals',
- Name => "New Pending Approval", # loc
- Description => "Notify Owners and AdminCcs of new items pending their approval", # loc
- Content => 'Subject: New Pending Approval: {$Ticket->Subject}
-
-Greetings,
-
-There is a new item pending your approval: "{$Ticket->Subject()}",
-a summary of which appears below.
-
-Please visit {$RT::WebURL}Approvals/Display.html?id={$Ticket->id}
-to approve or reject this ticket, or {$RT::WebURL}Approvals/ to
-batch-process all your pending approvals.
-
--------------------------------------------------------------------------
-{$Transaction->Content()}
-'
- },
-);
-
-# }}}
-
-1;
-
-@ScripActions = (
- { Name => 'Open Tickets',
- Description => 'Open tickets on correspondence',
- ExecModule => 'AutoOpen' },
-
-);
-
- @Scrips = (
- { ScripCondition => 'On Correspond',
- ScripAction => 'Open Tickets',
- Template => 'Blank',
- Queue => '0'
- },
- { ScripCondition => 'On Create',
- ScripAction => 'AutoReply To Requestors',
- Template => 'AutoReply' },
- { ScripCondition => 'On Create',
- ScripAction => 'Notify AdminCcs',
- Template => 'Transaction' },
- { ScripCondition => 'On Correspond',
- ScripAction => 'Notify AdminCcs',
- Template => 'Admin Correspondence' },
- { ScripCondition => 'On Correspond',
- ScripAction => 'Notify Requestors And Ccs',
- Template => 'Correspondence' },
- { ScripCondition => 'On Correspond',
- ScripAction => 'Notify Other Recipients',
- Template => 'Correspondence' },
- { ScripCondition => 'On Comment',
- ScripAction => 'Notify AdminCcs As Comment',
- Template => 'Admin Comment' },
- { ScripCondition => 'On Comment',
- ScripAction => 'Notify Other Recipients As Comment',
- Template => 'Correspondence' },
- { ScripCondition => 'On Resolve',
- ScripAction => 'Notify Requestors',
- Template => 'Resolved' },
-
-
- {
- Description => "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval", # loc
- Queue => '___Approvals',
- ScripCondition => 'On Create',
- ScripAction => 'Notify AdminCcs',
- Template => 'New Pending Approval'
- },
- {
- Description => "If an approval is rejected, reject the original and delete pending approvals", # loc
- Queue => '___Approvals',
- ScripCondition => 'On Status Change',
- ScripAction => 'User Defined',
- CustomCommitCode => q[
-# ------------------------------------------------------------------- #
-return(1) unless ( lc($self->TransactionObj->NewValue) eq "rejected" or
- lc($self->TransactionObj->NewValue) eq "deleted" );
-
-my $links = $self->TicketObj->DependedOnBy;
-foreach my $link (@{ $links->ItemsArrayRef }) {
- my $obj = $link->BaseObj;
- if ($obj->QueueObj->IsActiveStatus($obj->Status)) {
- if ($obj->Type eq 'ticket') {
- $obj->Correspond(
- Content => $self->loc("Your request was rejected."),
- );
- $obj->SetStatus(
- Status => 'rejected',
- Force => 1,
- );
- }
- else {
- $obj->SetStatus(
- Status => 'deleted',
- Force => 1,
- );
- }
- }
-}
-
-$links = $self->TicketObj->DependsOn;
-foreach my $link (@{ $links->ItemsArrayRef }) {
- my $obj = $link->TargetObj;
- if ($obj->QueueObj->IsActiveStatus($obj->Status)) {
- $obj->SetStatus(
- Status => 'deleted',
- Force => 1,
- );
- }
-}
-
-return 1;
-# ------------------------------------------------------------------- #
- ],
- CustomPrepareCode => '1',
- Template => 'Admin Comment',
- },
- {
- Description => "When a ticket has been approved by any approver, add correspondence to the original ticket", # loc
- Queue => '___Approvals',
- ScripCondition => 'On Resolve',
- ScripAction => 'User Defined',
- CustomPrepareCode => 'return(1);',
- CustomCommitCode => q[
-# ------------------------------------------------------------------- #
-return(1) unless ($self->TicketObj->Type eq 'approval');
-
-foreach my $obj ($self->TicketObj->AllDependedOnBy( Type => 'ticket' )) {
- $obj->Correspond(
- Content => $self->loc( "Your request has been approved by [_1]. Other approvals may still be pending.", # loc
- $self->TransactionObj->CreatorObj->Name,
- ) . "\n" . $self->loc( "Approver's notes: [_1]", # loc
- $self->TicketObj->Transactions->Last->Content,
- ),
- _reopen => 0,
- );
-}
-
-return 1;
-# ------------------------------------------------------------------- #
- ],
- Template => 'Admin Comment'
- },
- {
- Description => "When a ticket has been approved by all approvers, add correspondence to the original ticket", # loc
- Queue => '___Approvals',
- ScripCondition => 'On Resolve',
- ScripAction => 'User Defined',
- CustomPrepareCode => 'return(1);',
- CustomCommitCode => q[
-# ------------------------------------------------------------------- #
-# Find all the tickets that depend on this (that this is approving)
-
-my $Ticket = $self->TicketObj;
-my @TOP = $Ticket->AllDependedOnBy( Type => 'ticket' );
-my $links = $Ticket->DependedOnBy;
-
-while (my $link = $links->Next) {
- my $obj = $link->BaseObj;
- next if ($obj->HasUnresolvedDependencies( Type => 'approval' ));
-
- if ($obj->Type eq 'ticket') {
- $obj->Correspond(
- Content => $self->loc("Your request has been approved."),
- _reopen => 0,
- );
- }
- elsif ($obj->Type eq 'code') {
- my $code = $obj->Transactions->First->Content;
- my $rv;
-
- foreach my $TOP (@TOP) {
- local $@;
- $rv++ if eval $code;
- $RT::Logger->error("Cannot eval code: $@") if $@;
- }
-
- if ($rv or !@TOP) {
- $obj->SetStatus( Status => 'resolved', Force => 1,);
- }
- else {
- $obj->SetStatus( Status => 'rejected', Force => 1,);
- }
- }
-}
-
-return 1;
-# ------------------------------------------------------------------- #
- ],
- Template => 'Admin Comment',
- },
-);
-
-# }}}
-
diff --git a/rt/html/Admin/Elements/ModifyQueue b/rt/html/Admin/Elements/ModifyQueue
deleted file mode 100644
index 36f9ce17f..000000000
--- a/rt/html/Admin/Elements/ModifyQueue
+++ /dev/null
@@ -1,78 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<& /Elements/TitleBoxStart, title => loc('Editing Configuration for queue [_1]', $QueueObj->Id) &>
-
-<FORM ACTION="<%$RT::WebPath%>/Admin/Queues/Modify.html" METHOD=POST>
-<INPUT TYPE=HIDDEN NAME=id VALUE="<%$QueueObj->Id%>">
-<TABLE>
-<TR><TD ALIGN=RIGHT>
-<&|/l&>Queue Name</&>:
-</TD>
-<TD><INPUT name="Name" value="<%$QueueObj->Name%>"></TD>
-</TR><TR>
-<TD ALIGN=RIGHT>
-<&|/l&>Description</&>:</TD><TD COLSPAN=3><INPUT name="Description" value="<%$QueueObj->Description%>" size=60></TD></TR>
-<TR>
-<TD ALIGN=RIGHT>
-<&|/l&>Correspondence Address</&>:
-</TD><TD>
-<INPUT name="CorrespondAddress" value="<%$QueueObj->CorrespondAddress%>">
-</TD>
-<TD ALIGN=RIGHT>
-
-<&|/l&>Comment Address</&>: </TD><TD>
-<INPUT NAME="CommentAddress" value="<%$QueueObj->CommentAddress%>">
-</TD>
-</TR><TR>
-
-<TD ALIGN=RIGHT>
-<&|/l&>Priority starts at</&>:
-</TD><TD><INPUT NAME="InitialPriority" value="<%$QueueObj->InitialPriority %>">
-</TD>
-<TD ALIGN=RIGHT>
-<&|/l&>Over time, priority moves toward</&>:
-</TD><TD><INPUT NAME="FinalPriority" value="<%$QueueObj->FinalPriority %>">
-</TD>
-</TR>
-<TR>
-<TD ALIGN=RIGHT>
-<&|/l&>Requests should be due in</&>:
-</TD><TD>
-<INPUT NAME="DefaultDueIn" VALUE="<%$QueueObj->DefaultDueIn%>"> <&|/l&>days</&>.
-</TD>
-</TR>
-</TABLE>
-<& /Elements/Submit, Label => loc('Save Changes') &>
-</form>
-<& /Elements/TitleBoxEnd &>
-
-<%INIT>
-
-</%INIT>
-
-<%ARGS>
-
-
-$QueueObj => undef
-</%ARGS>
diff --git a/rt/html/Admin/Elements/ModifyUser b/rt/html/Admin/Elements/ModifyUser
deleted file mode 100644
index 2faefefaa..000000000
--- a/rt/html/Admin/Elements/ModifyUser
+++ /dev/null
@@ -1,99 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<& /Elements/TitleBoxStart, title => loc('Editing Configuration for user [_1]', $UserObj->Name) &>
-
-<FORM ACTION="<%$RT::WebPath%>/Admin/Users/Modify.html" METHOD=POST>
-<INPUT TYPE=HIDDEN NAME=id VALUE="<%$UserObj->Id%>">
-
-<&|/l&>Name</&>: <input name="Name" value="<%$UserObj->Name%>">
-<BR>
-<&|/l&>New Password</&>: <input type=password name="Pass1"><BR>
-<&|/l&>Retype Password</&>: <input type=password name="Pass2"><BR>
-
-<&|/l&>Comments</&>: <TEXTAREA name="Comments" COLS=80 ROWS=5 WRAP=VIRTUAL>
-<%$UserObj->Comments%></TEXTAREA>
-
-<BR>
-<&|/l&>Signature</&>: <TEXTAREA COLS=80 ROWS=5 name="Signature" WRAP=HARD>
-<%$UserObj->Signature%></TEXTAREA>
-<BR>
-<&|/l&>EmailAddress</&>: <input name="EmailAddress" value="<%$UserObj->EmailAddress%>">
-<BR>
-<&|/l&>FreeformContactInfo</&>: <input name="FreeformContactInfo" value="<%$UserObj->FreeformContactInfo%>">
-<BR>
-<&|/l&>Organization</&>: <input name="Organization" value="<%$UserObj->Organization%>">
-<BR>
-<&|/l&>RealName</&>: <input name="RealName" value="<%$UserObj->RealName%>">
-<BR>
-<&|/l&>NickName</&>: <input name="NickName" value="<%$UserObj->NickName%>">
-<BR>
-<&|/l&>Lang</&>: <input name="Lang" value="<%$UserObj->Lang%>">
-<BR>
-<&|/l&>EmailEncoding</&>: <input name="EmailEncoding" value="<%$UserObj->EmailEncoding%>">
-<BR>
-<&|/l&>WebEncoding</&>: <input name="WebEncoding" value="<%$UserObj->WebEncoding%>">
-<BR>
-<&|/l&>ExternalContactInfoId</&>: <input name="ExternalContactInfoId" value="<%$UserObj->ExternalContactInfoId%>">
-<BR>
-<&|/l&>ContactInfoSystem</&>: <input name="ContactInfoSystem" value="<%$UserObj->ContactInfoSystem%>">
-<BR>
-<&|/l&>UnixUsername</&>: <input name="Gecos" value="<%$UserObj->Gecos%>">
-<BR>
-<&|/l&>ExternalAuthId</&>: <input name="ExternalAuthId" value="<%$UserObj->ExternalAuthId%>">
-<BR>
-<&|/l&>AuthSystem</&>: <input name="AuthSystem" value="<%$UserObj->AuthSystem%>">
-<BR>
-<&|/l&>HomePhone</&>: <input name="HomePhone" value="<%$UserObj->HomePhone%>">
-<BR>
-<&|/l&>WorkPhone</&>: <input name="WorkPhone" value="<%$UserObj->WorkPhone%>">
-<BR>
-<&|/l&>MobilePhone</&>: <input name="MobilePhone" value="<%$UserObj->MobilePhone%>">
-<BR>
-<&|/l&>PagerPhone</&>: <input name="PagerPhone" value="<%$UserObj->PagerPhone%>">
-<BR>
-<&|/l&>Address1</&>: <input name="Address1" value="<%$UserObj->Address1%>">
-<BR>
-<&|/l&>Address2</&>: <input name="Address2" value="<%$UserObj->Address2%>">
-<BR>
-<&|/l&>City</&>: <input name="City" value="<%$UserObj->City%>">
-<BR>
-<&|/l&>State</&>: <input name="State" value="<%$UserObj->State%>">
-<BR>
-<&|/l&>Zip</&>: <input name="Zip" value="<%$UserObj->Zip%>">
-<BR>
-<&|/l&>Country</&>: <input name="Country" value="<%$UserObj->Country%>">
-<BR>
-<& /Elements/Submit, Label => loc('Save Changes') &>
-</form>
-<& /Elements/TitleBoxEnd &>
-
-<%INIT>
-
-</%INIT>
-
-<%ARGS>
-
-
-$UserObj => undef
-</%ARGS>
diff --git a/rt/html/Admin/Global/CustomField.html b/rt/html/Admin/Global/CustomField.html
deleted file mode 100644
index 3871d8998..000000000
--- a/rt/html/Admin/Global/CustomField.html
+++ /dev/null
@@ -1,86 +0,0 @@
-%# {{{ BEGIN BPS TAGGED BLOCK
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC
-%# <jesse@bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# }}} END BPS TAGGED BLOCK
-<& /Admin/Elements/Header, Title => $title &>
-<& /Admin/Elements/SystemTabs,
- current_tab => 'Admin/Global/CustomFields.html',
- current_subtab => $current_subtab,
- subtabs => $subtabs,
- Title => $title &>
-
-<& /Admin/Elements/EditCustomField, title => $title, %ARGS &>
-
-<%INIT>
-my ($title, $current_subtab);
-
-my $subtabs = {
- A => { title => loc('Select custom field'),
- path => "Admin/Global/CustomFields.html"
- },
- B => { title => loc('New custom field'),
- path => "Admin/Global/CustomField.html?create=1&Queue=0",
- separator => 1,
- }
- };
-if ( $ARGS{'create'} ) {
- $current_subtab = "Admin/Global/CustomField.html?create=1&Queue=0";
- $title = loc('Create a CustomField which applies to all queues');
-}
-else {
- $current_subtab =
- "Admin/Global/CustomField.html?CustomField=" . $CustomField . "&Queue=0";
- $title = loc('Modify a CustomField which applies to all queues');
- $subtabs->{"C"} = {
- title => loc( 'Custom Field #[_1]', $CustomField ),
- path => "Admin/Global/CustomField.html?CustomField=" . $CustomField . "&Queue=0"
- };
-}
-</%INIT>
-<%ARGS>
-$CustomField => undef
-</%ARGS>
-<%ATTR>
-AutoFlush => 0
-</%ATTR>
diff --git a/rt/html/Admin/Global/CustomFields.html b/rt/html/Admin/Global/CustomFields.html
deleted file mode 100644
index 593040218..000000000
--- a/rt/html/Admin/Global/CustomFields.html
+++ /dev/null
@@ -1,69 +0,0 @@
-%# {{{ BEGIN BPS TAGGED BLOCK
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC
-%# <jesse@bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# }}} END BPS TAGGED BLOCK
-<& /Admin/Elements/Header, Title => $title &>
-<& /Admin/Elements/SystemTabs,
- current_tab => 'Admin/Global/CustomFields.html',
- current_subtab => 'Admin/Global/CustomFields.html',
- subtabs => $subtabs,
- Title => $title &>
-
-<& /Admin/Elements/EditCustomFields, title => $title, %ARGS &>
-
-<%INIT>
-my $subtabs = {
- A => { title => loc('Select custom field'),
- path => "Admin/Global/CustomFields.html"
- },
- B => { title => loc('New custom field'),
- path => "Admin/Global/CustomField.html?create=1&Queue=0",
- separator => 1,
- }
- };
-my $title = loc("Modify Custom Fields which apply to all queues");
-</%INIT>
-<%ARGS>
-$id => undef
-</%ARGS>
diff --git a/rt/html/Admin/Users/Modify.html b/rt/html/Admin/Users/Modify.html
index 75a7696a5..1be96f695 100644
--- a/rt/html/Admin/Users/Modify.html
+++ b/rt/html/Admin/Users/Modify.html
@@ -105,6 +105,12 @@
</table>
</&>
<br />
+
+<&| /Widgets/TitleBox, title => loc('Customers') &>
+<& /Elements/EditCustomers, Object => $UserObj, CustomerString=> $CustomerString, ServiceString => $ServiceString &>
+</&>
+<br />
+
<&| /Widgets/TitleBox, title => loc('Access control') &>
<input type="hidden" class="hidden" name="SetEnabled" value="1" />
<input type="checkbox" class="checkbox" name="Enabled" value="1" <%$EnabledChecked%> />
@@ -339,6 +345,8 @@ if ($UserObj->Id && $id ne 'new') {
push (@results,@fieldresults);
push @results, ProcessObjectCustomFieldUpdates( ARGSRef => \%ARGS, Object => $UserObj );
+ #deal with freeside customer links
+ push @results, ProcessObjectCustomers( ARGSRef => \%ARGS, Object => $UserObj );
# {{{ Deal with special fields: Privileged, Enabled
if ( $SetPrivileged and $Privileged != $UserObj->Privileged ) {
@@ -430,4 +438,8 @@ $Country => undef
$Pass1 => undef
$Pass2=> undef
$Create=> undef
+$OnlySearchForCustomers => undef
+$OnlySearchForServices => undef
+$CustomerString => undef
+$ServiceString => undef
</%ARGS>
diff --git a/rt/html/Admin/Users/Prefs.html b/rt/html/Admin/Users/Prefs.html
deleted file mode 100644
index 0bba9fadd..000000000
--- a/rt/html/Admin/Users/Prefs.html
+++ /dev/null
@@ -1,122 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<& /Elements/Header, Title => loc("User view") &>
-
-<& /Elements/ViewUser, User=>$u &>
-
-<h2 class="title"><%loc("User view")%></h2>
-
-%if ($session{CurrentUser} && ($session{CurrentUser}->Id == $id)) {
- <& /Elements/TitleBoxStart, title => loc('Signature') &>
-<form method=post>
-<input type="hidden" name="id" value=<%$id%>>
-<TEXTAREA COLS=72 ROWS=4 WRAP=HARD NAME="Signature"><% $u->Signature %></TEXTAREA><br><br>
-<input type="submit" value="<&|/l&>Update signature</&>">
-</form>
- <& /Elements/TitleBoxEnd &>
- <form method=post>
- <&|/l&>Open tickets (from listing) in another window</&>: <input type="checkbox" name="NewWindowOption" <%exists $session{NewWindowOption} && "CHECKED"%>><br>
- <&|/l&>Open tickets (from listing) in a new window</&>: <input type="checkbox" name="AlwaysNewWindowOption" <%exists $session{AlwaysNewWindowOption} && "CHECKED"%>><br>
- <input type="submit" name="NewWindowSetting" value="<&|/l&>New window setting</&>">
- </form>
-%}
-
- <& /Elements/TitleBoxStart, title => loc('Email') &>
-<form method=post>
-<input type="hidden" name="id" value="<%$id%>">
-<input name="Email" value="<% $u->EmailAddress %>"><input type="submit" value="<&|/l&>Update email</&>">
-</form>
- <& /Elements/TitleBoxEnd &>
- <& /Elements/TitleBoxStart, title => loc('Real Name') &>
-<form method=post>
-<input type="hidden" name="id" value="<%$id%>">
-<input name="RealName" value="<% $u->RealName %>"><input type="submit" value="<&|/l&>Update name</&>">
-</form>
- <& /Elements/TitleBoxEnd &>
-
- <& /Elements/TitleBoxStart, title => loc('User ID') &>
-<form method=post>
-<input type="hidden" name="id" value="<%$id%>">
-<input name="Name" value="<% $u->Name %>"><input type="submit" value="<&|/l&>Update ID</&>">
-</form>
- <& /Elements/TitleBoxEnd &>
-
-%# TODO: alternative email addresses + merging users
-
-<%ARGS>
-$id => $session{CurrentUser} ? $session{CurrentUser}->Id : 0
-$Signature => undef
-$Email => undef
-$RealName => undef
-$Name => undef
-</%ARGS>
-
-<%INIT>
-require RT::User;
-my $u=RT::User->new($session{CurrentUser});
-$u->Load($id) || die loc("Couldn't load that user ([_1])", $id);
-if ($Signature) {
-my ($val, $msg)=$u->SetSignature($Signature);
-$RT::Logger->log(level=>($val ? 'info' : 'error'), message=>$msg);
-}
-
-if ($Email) {
-my ($val, $msg)=$u->SetEmailAddress($Email);
-$RT::Logger->log(level=>($val ? 'info' : 'error'), message=>$msg);
-}
-
-if ($RealName) {
-my ($val, $msg)=$u->SetRealName($RealName);
-$RT::Logger->log(level=>($val ? 'info' : 'error'), message=>$msg);
-}
-
-if ($Name) {
-my ($val, $msg)=$u->SetName($Name);
-$RT::Logger->log(level=>($val ? 'info' : 'error'), message=>$msg);
-}
-
-if ($ARGS{NewWindowSetting}) {
-if ($ARGS{NewWindowOption}) {
-$session{NewWindowOption}=1;
-} else {
-delete $session{NewWindowOption};
-}
-if ($ARGS{AlwaysNewWindowOption}) {
-$session{NewWindowOption}=1;
-$session{AlwaysNewWindowOption}=1;
-} else {
-delete $session{AlwaysNewWindowOption};
-}
-}
-
-</%INIT>
-
-
-
-
-
-
-
-
-
diff --git a/rt/html/Callbacks/ActivityReports/Elements/Tabs/Default b/rt/html/Callbacks/ActivityReports/Elements/Tabs/Default
new file mode 100644
index 000000000..f85d2e010
--- /dev/null
+++ b/rt/html/Callbacks/ActivityReports/Elements/Tabs/Default
@@ -0,0 +1,7 @@
+<%init>
+if ($ARGS{current_toptab} eq "Tools/Offline.html") {
+ $ARGS{tabs}{r} ||= { path => 'Reports/Activity/index.html',
+ title => 'Reports',
+ };
+}
+</%init> \ No newline at end of file
diff --git a/rt/html/Callbacks/ActivityReports/NoAuth/webrt.css/Default b/rt/html/Callbacks/ActivityReports/NoAuth/webrt.css/Default
new file mode 100644
index 000000000..30480f7b6
--- /dev/null
+++ b/rt/html/Callbacks/ActivityReports/NoAuth/webrt.css/Default
@@ -0,0 +1,71 @@
+table.miniplot {
+ width: 100%;
+ border-collapse: collapse;
+}
+table.miniplot td {
+ margin: 0;
+ padding: 0;
+ border-bottom: 1px solid black;
+}
+table.miniplot .graph {
+ margin-left: auto;
+ margin-right: auto;
+ position: relative;
+ width: 60px;
+}
+table.miniplot .graph ul {
+ height: 100px;
+ margin: 0;
+ padding: 0;
+}
+table.miniplot .graph ul li {
+ list-style: none;
+ position: absolute;
+ bottom: 0px;
+ padding: 0 !important;
+ margin: 0 !important;
+ border-bottom: none;
+}
+table.miniplot .graph ul li .data {
+ display: none;
+}
+
+.miniplot .demoblock { margin: 0 10px; padding: 0 30px; }
+
+.miniplot .c1 { border: 2px solid #990000; background: #ff0000; }
+.miniplot .c2 { border: 2px solid #996600; background: #ff9900; }
+.miniplot .c3 { border: 2px solid #009900; background: #00ff00; }
+.miniplot .c4 { border: 2px solid #009999; background: #00ffff; }
+.miniplot .c5 { border: 2px solid #000099; background: #0000ff; }
+.miniplot .c6 { border: 2px solid #990099; background: #ff00ff; }
+graph .c5 { border: 2px solid #000099; background: #0000ff; }
+.graph .c6 { border: 2px solid #990099; background: #ff00ff; }
+
+tr.titlerow th {
+
+ border-bottom: solid black 1px;
+ margin: 0;
+ font-size:80%;
+ text-wrap: none;
+
+}
+
+tr.grandtotal td{
+ border-top: 1px solid black;
+}
+
+tr.grandtotal th{
+ border-top: 1px solid black;
+}
+
+th.label {
+ align: left;
+
+}
+
+table.miniplot th.legend {
+ font-style: normal;
+ font-size: 80%;
+
+}
+
diff --git a/rt/html/Callbacks/ActivityReports/Search/Results.html/SearchActions b/rt/html/Callbacks/ActivityReports/Search/Results.html/SearchActions
new file mode 100644
index 000000000..4775a9af3
--- /dev/null
+++ b/rt/html/Callbacks/ActivityReports/Search/Results.html/SearchActions
@@ -0,0 +1,7 @@
+<a href="<% $RT::WebPath %>/Reports/Activity/index.html?<% $QueryString %>">Generate reports</a>
+<%init>
+use YAML;
+my %args = $m->caller_args(2);
+
+my $QueryString = $m->comp('/Elements/QueryString', query => $args{Query});
+</%init> \ No newline at end of file
diff --git a/rt/html/Callbacks/RT-WebCronTool/Elements/Tabs/Default b/rt/html/Callbacks/RT-WebCronTool/Elements/Tabs/Default
new file mode 100644
index 000000000..db74ced2d
--- /dev/null
+++ b/rt/html/Callbacks/RT-WebCronTool/Elements/Tabs/Default
@@ -0,0 +1,13 @@
+%# The day after tomorrow is the third day of the rest of your life.
+<%INIT>
+if ($session{'CurrentUser'}->UserObj->HasRight(
+ Right => 'SuperUser',
+ Object => $RT::System,
+)) {
+ $toptabs->{'ZZ-RT-WebCronTool'} = { title =>loc("Web CronTool"),
+ path => "Developer/CronTool/index.html" };
+}
+</%init>
+<%args>
+$toptabs =>undef
+</%args>
diff --git a/rt/html/Callbacks/kStatistics/Elements/Tabs/Default b/rt/html/Callbacks/kStatistics/Elements/Tabs/Default
new file mode 100644
index 000000000..d4ca2b95e
--- /dev/null
+++ b/rt/html/Callbacks/kStatistics/Elements/Tabs/Default
@@ -0,0 +1,11 @@
+<%init>
+use RTx::Statistics;
+if (($Statistics::RestrictAccess == 0) || ($session{'CurrentUser'}->HasRight( Right => 'ShowConfigTab',
+ Object => $RT::System ))) {
+ $toptabs->{'ZZ-RTx-STATS'} = { title => 'RTx-Statistics',
+ path => "RTx/Statistics/index.html" };
+}
+</%init>
+<%args>
+ $toptabs =>undef
+</%args>
diff --git a/rt/html/Developer/CronTool/autohandler b/rt/html/Developer/CronTool/autohandler
new file mode 100644
index 000000000..7daa09e8d
--- /dev/null
+++ b/rt/html/Developer/CronTool/autohandler
@@ -0,0 +1,9 @@
+%# All theoretical chemistry is really physics;
+%# and all theoretical chemists know it.
+%# -- Richard P. Feynman
+<%INIT>
+$m->call_next(%ARGS) if $session{'CurrentUser'}->UserObj->HasRight(
+ Right => 'SuperUser',
+ Object => $RT::System,
+);
+</%INIT>
diff --git a/rt/html/Developer/CronTool/index.html b/rt/html/Developer/CronTool/index.html
new file mode 100644
index 000000000..67c9e5634
--- /dev/null
+++ b/rt/html/Developer/CronTool/index.html
@@ -0,0 +1,116 @@
+% if ($@) {
+<P><FONT Color="red"><% $@ %></FONT></P>
+% }
+% if (!$NoUI) {
+<HR>
+<FORM Action="index.html" Method="POST">
+<TABLE>
+% foreach my $class (qw( Search Condition Action )) {
+<TR><TH>
+<% loc($class) %>
+</TH><TD>
+<SELECT NAME="<% $class %>">
+% require File::Find;
+% my @modules;
+% File::Find::find(sub {
+% push @modules, $1 if /^(?!Generic|UserDefined)(\w+)\.pm$/i;
+% }, grep -d, map "$_/RT/$class", @INC);
+<OPTION <% $ARGS{$class} ? '' : 'SELECTED' %>></OPTION>
+% foreach my $module (sort @modules) {
+% my $fullname = "RT::$class\::$module";
+ <OPTION VALUE="<% $fullname %>" <% ($fullname eq $ARGS{$class}) ? 'SELECTED' : '' %>><% $module %></OPTION>
+% }
+</SELECT>
+</TD><TH>
+<&|/l&>Parameter</&>
+</TH><TD>
+<INPUT NAME="<% $class %>Arg" VALUE="<% $ARGS{$class.'Arg'} %>">
+</TD></TR>
+% }
+<TR>
+<TD COLSPAN="4" ALIGN="Right">
+<LABEL>
+<INPUT TYPE="CheckBox" NAME="Verbose" <% $Verbose ? 'CHECKED' : '' %>><&|/l&>Verbose</&>
+</LABEL>
+<INPUT TYPE="Submit" VALUE="<&|/l&>Run</&>">
+</TD>
+</TABLE>
+</FORM>
+<HR>
+% }
+<%INIT>
+$m->print("<H1>", loc("Web CronTool"), "</H1>");
+if ($Search) {
+ my $load_module = sub {
+ my $modname = $_[0];
+ $modname =~ s{::}{/}g;
+ require "$modname.pm" or die (
+ loc( "Failed to load module [_1]. ([_2])", $_[0], $@ ) . "\n"
+ );
+ };
+ $m->print(loc("Starting..."), "<UL>");
+ eval {
+ $load_module->($Search);
+ $load_module->($Action) if $Action;
+ $load_module->($Condition) if $Condition;
+
+ if ($TemplateId and !$TemplateObj) {
+ $TemplateObj = RT::Template->new($RT::Nobody);
+ $TemplateObj->LoadById($TemplateId);
+ }
+
+ my $tickets = RT::Tickets->new($RT::SystemUser);
+ my $search = $Search->new( TicketsObj => $tickets, Argument => $SearchArg );
+ $search->Prepare;
+ my $tickets_found = $search->TicketsObj;
+
+ #for each ticket we've found
+ while ( my $ticket = $tickets_found->Next ) {
+ $m->print("<LI>" . $ticket->Id . ": ") if $Verbose;
+ $m->print(loc("Checking...")) if $Verbose;
+
+ # perform some more advanced check
+ if ($Condition) {
+ my $ConditionObj = $Condition->new(
+ TicketObj => $ticket,
+ Argument => $ConditionArg
+ );
+
+ # if the condition doesn't apply, get out of here
+ next unless ( $ConditionObj->IsApplicable );
+ $m->print(loc("Condition matches...")) if $Verbose;
+ }
+
+ if ($Action) {
+ #prepare our action
+ my $ActionObj = $Action->new(
+ TicketObj => $ticket,
+ TemplateObj => $TemplateObj,
+ Argument => $ActionArg
+ );
+
+ #if our preparation, move onto the next ticket
+ next unless ( $ActionObj->Prepare );
+ $m->print(loc("Action prepared...")) if $Verbose;
+
+ #commit our action.
+ next unless ( $ActionObj->Commit );
+ $m->print(loc("Action committed.")) if $Verbose;
+ }
+ }
+ };
+ $m->print('</UL>', loc("Finished."));
+}
+</%INIT>
+<%ARGS>
+$Search => undef
+$SearchArg => undef
+$Condition => undef
+$ConditionArg => undef
+$Action => undef
+$ActionArg => undef
+$TemplateId => undef
+$TemplateObj => undef
+$Verbose => 1
+$NoUI => 0
+</%ARGS>
diff --git a/rt/html/Elements/AddCustomers b/rt/html/Elements/AddCustomers
new file mode 100644
index 000000000..aaf8ca8ba
--- /dev/null
+++ b/rt/html/Elements/AddCustomers
@@ -0,0 +1,59 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%# Copyright (c) 2008 Freeside Internet Services, Inc.
+%#
+%# 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.
+<BR>
+<%$msg%><br>
+
+% if (@Customers) {
+
+<br><i>(Check box to link)<i>
+<table>
+% foreach my $customer (@Customers) {
+<tr>
+ <td>
+ <input type="checkbox" name="Object-AddCustomer-<% $customer->{'custnum'} %>" VALUE="1" <% scalar(@Customers) == 1 ? 'CHECKED' : '' %>>
+ <A HREF="<%$freeside_url%>/view/cust_main.cgi?<% $customer->{'custnum'} %>"><% &RT::URI::freeside::small_custview($customer->{'custnum'}, &RT::URI::freeside::FreesideGetConfig('countrydefault'), 1) |n %>
+ </td>
+</tr>
+% }
+</table>
+
+% }
+
+<%INIT>
+my ($msg);
+
+my $freeside_url = &RT::URI::freeside::FreesideURL();
+
+warn "/Elements/AddCustomers called with CustomerString $CustomerString\n"
+ if $Debug;
+
+my @Customers = ();
+if ( $CustomerString ) {
+ @Customers = &RT::URI::freeside::smart_search( 'search' => $CustomerString );
+}
+
+my @Services = ();
+if ($ServiceString) {
+ @Services = (); #service_search();
+}
+
+warn "/Elements/AddCustomers displaying ". scalar(@Customers). " customers\n"
+ if $Debug;
+
+</%INIT>
+
+<%ARGS>
+$CustomerString => undef
+$ServiceString => undef
+$Debug => 0
+</%ARGS>
diff --git a/rt/html/Elements/EditCustomers b/rt/html/Elements/EditCustomers
new file mode 100644
index 000000000..68efb5f40
--- /dev/null
+++ b/rt/html/Elements/EditCustomers
@@ -0,0 +1,63 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%# Copyright (c) 2008 Freeside Internet Services, Inc.
+%#
+%# 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.
+<TABLE width=100%>
+ <TR>
+ <TD VALIGN=TOP WIDTH=50%>
+ <h3><&|/l&>Current Customers</&></h3>
+
+<table>
+ <tr>
+ <td><i><&|/l&>(Check box to disassociate)</&></i></td>
+ </tr>
+ <tr>
+ <td class="value">
+% foreach my $link ( @{ $Object->Customers->ItemsArrayRef } ) {
+
+ <INPUT TYPE=CHECKBOX NAME="DeleteLink--<%$link->Type%>-<%$link->Target%>">
+%# <& ShowLink, URI => $link->TargetURI &><br>
+ <A HREF="<% $link->TargetURI->Resolver->HREF %>"><% $link->TargetURI->Resolver->AsStringLong |n %></A>
+ <BR>
+% }
+ </td>
+ </tr>
+</table>
+
+</TD>
+
+<TD VALIGN=TOP>
+<h3><&|/l&>New Customer Links</&></h3>
+<&|/l&>Find customer</&><BR>
+<input name="CustomerString">
+<input type=submit name="OnlySearchForCustomers" value="<&|/l&>Go!</&>">
+<br><i>cust #, name, company or phone</i>
+<BR>
+%#<BR>
+%#<&|/l&>Find service</&><BR>
+%#<input name="ServiceString">
+%#<input type=submit name="OnlySearchForServices" value="<&|/l&>Go!</&>">
+%#<br><i>username, username@domain, domain, or IP address</i>
+%#<BR>
+
+<& AddCustomers, Object => $Object,
+ CustomerString => $CustomerString,
+ ServiceString => $ServiceString, &>
+
+</TD>
+</TR>
+</TABLE>
+
+<%ARGS>
+$CustomerString => undef
+$ServiceString => undef
+$Object => undef
+</%ARGS>
diff --git a/rt/html/Elements/Footer b/rt/html/Elements/Footer
index c3d766258..914bbbd3b 100644
--- a/rt/html/Elements/Footer
+++ b/rt/html/Elements/Footer
@@ -47,25 +47,12 @@
%# END BPS TAGGED BLOCK }}}
%# End of div#body from /Elements/PageLayout
</div>
+</td>
+</tr>
+<tr>
+<td>
<& /Elements/Callback, %ARGS &>
-<div id="footer">
- <p id="time">
- <span><&|/l&>Time to display</&>: <%Time::HiRes::tv_interval( $m->{'rt_base_time'} )%></span>
- </p>
- <p id="bpscredits">
- <span>
-<&|/l, '&#187;&#124;&#171;', $RT::VERSION, '2009', '<a href="http://www.bestpractical.com?rt='.$RT::VERSION.'">Best Practical Solutions, LLC</a>', &>[_1] RT [_2] Copyright 1996-[_3] [_4].</&>
-</span>
-</p>
-% if (!$Menu) {
- <p id="legal">
-<&|/l&>Distributed under version 2 <a href="http://www.gnu.org/copyleft/gpl.html"> of the GNU GPL.</a></&><br />
-<&|/l, '<a href="mailto:sales@bestpractical.com">sales@bestpractical.com</a>' &>To inquire about support, training, custom development or licensing, please contact [_1].</&><br />
- </p>
-% }
-
-</div>
% if ($Debug >= 2 ) {
% require Data::Dumper;
% my $d = Data::Dumper->new([\%ARGS], [qw(%ARGS)]);
@@ -74,6 +61,10 @@
</pre>
% }
+</TD>
+</TR>
+</TABLE>
+
</body>
</html>
% $m->abort();
diff --git a/rt/html/Elements/FreesideInvoiceSearch b/rt/html/Elements/FreesideInvoiceSearch
new file mode 100644
index 000000000..3842b2ff9
--- /dev/null
+++ b/rt/html/Elements/FreesideInvoiceSearch
@@ -0,0 +1,20 @@
+% if ( $FS::CurrentUser::CurrentUser->access_right('View invoices') ) {
+
+ <form action="<% $RT::URI::freeside::URL %>/search/cust_bill.html" STYLE="margin:0">
+ <SCRIPT TYPE="text/javascript">
+ function clearhint_search_invoice (what) {
+ if ( what.value == '(inv #)' )
+ what.value = '';
+ }
+ </SCRIPT>
+ <input name="invnum" accesskey="0" VALUE="(inv #)" SIZE="4" onFocus="clearhint_search_invoice(this);" onClick="clearhint_search_invoice(this);" STYLE="text-align:right; margin-bottom:1px; font-family: Arial, Verdana, Helvetica, sans-serif;">
+
+% if ( $FS::CurrentUser::CurrentUser->access_right('List invoices') ) {
+ <A HREF="<% $RT::URI::freeside::URL %>search/report_cust_bill.html" STYLE="color: #ffffff; font-size: 70%; font-weight:normal">Advanced</A>
+% }
+ <BR>
+
+ <input type="submit" value="<&|/l&>Search invoices</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
+ </form>
+
+% }
diff --git a/rt/html/Elements/FreesideNewCust b/rt/html/Elements/FreesideNewCust
new file mode 100644
index 000000000..f60e99559
--- /dev/null
+++ b/rt/html/Elements/FreesideNewCust
@@ -0,0 +1,3 @@
+<form action="<% $RT::URI::freeside::URL %>/edit/cust_main.cgi" STYLE="margin:0">
+<INPUT TYPE="submit" VALUE="<&|/l&>New customer</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="vertical-align:bottom; font-size:100%">&nbsp;
+</FORM>
diff --git a/rt/html/Elements/FreesideSearch b/rt/html/Elements/FreesideSearch
new file mode 100644
index 000000000..8e609bb4b
--- /dev/null
+++ b/rt/html/Elements/FreesideSearch
@@ -0,0 +1,13 @@
+% if ( $FS::CurrentUser::CurrentUser->access_right('List customers') ) {
+<form action="<% $RT::URI::freeside::URL %>/search/cust_main.cgi" STYLE="margin:0">
+ <SCRIPT TYPE="text/javascript">
+ function clearhint_search_cust (what) {
+ if ( what.value == '(cust #, name, company or phone)' )
+ what.value = '';
+ }
+ </SCRIPT>
+<input name="search_cust" accesskey="0" VALUE="(cust #, name, company or phone)" SIZE="28" onFocus="clearhint_search_cust(this);" onClick="clearhint_search_cust(this);" STYLE="text-align:right; font-family: Arial, Verdana, Helvetica, sans-serif;"><BR>
+<A HREF="<% $RT::URI::freeside::URL %>/search/report_cust_main.html" STYLE="color: #ffffff; font-size: 70%; font-weight:normal">Advanced</A>
+<input type="submit" value="<&|/l&>Search customers</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
+</form>
+% }
diff --git a/rt/html/Elements/FreesideSvcSearch b/rt/html/Elements/FreesideSvcSearch
new file mode 100644
index 000000000..d68122da5
--- /dev/null
+++ b/rt/html/Elements/FreesideSvcSearch
@@ -0,0 +1,13 @@
+% if ( $FS::CurrentUser::CurrentUser->access_right('View customer services') ) {
+<form action="<% $RT::URI::freeside::URL %>/search/cust_svc.html" STYLE="margin:0">
+ <SCRIPT TYPE="text/javascript">
+ function clearhint_search_svc (what) {
+ if ( what.value == '(user, user@domain or domain)' )
+ what.value = '';
+ }
+ </SCRIPT>
+<input name="search_svc" accesskey="0" VALUE="(user, user@domain or domain)" SIZE="26" onFocus="clearhint_search_svc(this);" onClick="clearhint_search_svc(this);" STYLE="text-align:right; font-family: Arial, Verdana, Helvetica, sans-serif;"><BR>
+ <A NOTYET="<% $RT::URI::freeside::URL %>search/svc_Smarter.html" STYLE="color: #000000; font-size: 70%; font-weight:normal">Advanced</A>
+<input type="submit" value="<&|/l&>Search services</&>" CLASS="fsblackbutton" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%">
+</form>
+% }
diff --git a/rt/html/Elements/Header b/rt/html/Elements/Header
index 02450b1e0..571da6c90 100644
--- a/rt/html/Elements/Header
+++ b/rt/html/Elements/Header
@@ -45,70 +45,24 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<% include( '/elements/header.html', {
+ 'title' => $Title,
+ 'head' => $head,
+ 'nobr' => 1,
+ 'nocss' => 1,
+ }) |n
+%>
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
-
-<title><%$Title%></title>
-
-% if ($Refresh && $Refresh > 0) {
- <meta http-equiv="refresh" content="<%$Refresh%>" />
-% }
-
-<link rel="shortcut icon" href="<%$RT::WebImagesURL%>/favicon.png" type="image/png" />
-<link rel="stylesheet" href="<%$RT::WebPath%>/NoAuth/css/<% $RT::WebDefaultStylesheet %>/main-squished.css" type="text/css" media="all" />
-<link rel="stylesheet" href="<%$RT::WebPath%>/NoAuth/css/print.css" type="text/css" media="print" />
-
-% if ( $RSSAutoDiscovery ) {
- <link rel="alternate" href="<%$RSSAutoDiscovery%>" type="application/rss+xml" title="RSS RT Search" />
-% }
-
-<script type="text/javascript" src="<%$RT::WebPath%>/NoAuth/js/util.js"></script>
-<script type="text/javascript" src="<%$RT::WebPath%>/NoAuth/js/ahah.js"></script>
-<script type="text/javascript" src="<%$RT::WebPath%>/NoAuth/js/titlebox-state.js"></script>
-<script type="text/javascript"><!--
- onLoadHook("loadTitleBoxStates()");
-% if ( $Focus ) {
- onLoadHook("focusElementById('<% $Focus %>')");
-% }
-% if ( $onload ) {
- onLoadHook("<% $onload |n %>");
-% }
---></script>
-
-<& /Elements/Callback, _CallbackName => 'Head', %ARGS &>
-
-</head>
- <body<% $id && qq[ id="comp-$id"] |n %>>
-
-% if ($ShowBar) {
-<& /Elements/Logo &>
-
-<div id="quickbar">
- <div id="quick-personal">
- <span class="hide"><a href="#skipnav"><&|/l&>Skip Menu</&></a> | </span>
-% if ($session{'CurrentUser'}->Name) {
- <&|/l, "<span>".$session{'CurrentUser'}->Name."</span>" &>Logged in as [_1]</&>
-% if ($session{'CurrentUser'}->HasRight( Right => 'ModifySelf', Object => $RT::System )) {
- | <a href="<%$RT::WebPath%><%$Prefs%>"><&|/l&>Preferences</&></a>
-% }
-% } else {
- <&|/l&>Not logged in.</&>
-% }
- <& /Elements/Callback, %ARGS &>
-% unless (!$session{'CurrentUser'}->Name
-% or ($RT::WebExternalAuth and !$RT::WebFallbackToInternalAuth)) {
- | <a href="<%$RT::WebPath%>/NoAuth/Logout.html<%$URL ? "?URL=".$URL : ''%>"><&|/l&>Logout</&></a>
-% }
- </div>
-% }
+%# if ($session{'CurrentUser'}->HasRight( Right => 'ModifySelf', Object => $RT::System )) {
+%# | <a href="<%$RT::WebPath%><%$Prefs%>"><&|/l&>Preferences</&></a>
+%# }
<%INIT>
$r->headers_out->{'Pragma'} = 'no-cache';
$r->headers_out->{'Cache-control'} = 'no-cache';
+require RT::URI::freeside;
+
my $id = $m->request_comp->path;
$id =~ s|^/||g;
$id =~ s|/|-|g;
@@ -116,6 +70,45 @@ $id =~ s|\.html$||g;
$id =~ s|index$||g
if $id ne 'index';
$id =~ s|-$||g;
+
+my $head = '';
+
+if ($Refresh && $Refresh > 0) {
+ $head .= '<meta http-equiv="refresh" content="$Refresh" />';
+}
+
+$head .= <<END;
+<link rel="shortcut icon" href="$RT::WebImagesURL/favicon.png" type="image/png" />
+<link rel="stylesheet" href="$RT::WebPath/NoAuth/css/$RT::WebDefaultStylesheet/main-squished.css" type="text/css" media="all" />
+<link rel="stylesheet" href="$RT::WebPath/NoAuth/css/print.css" type="text/css" media="print" />
+END
+
+if ( $RSSAutoDiscovery ) {
+ $head .= qq(<link rel="alternate" href="$RSSAutoDiscovery" type="application/rss+xml" title="RSS RT Search" />);
+}
+
+$head .= <<END;
+<script type="text/javascript" src="$RT::WebPath/NoAuth/js/util.js"></script>
+<script type="text/javascript" src="$RT::WebPath/NoAuth/js/ahah.js"></script>
+<script type="text/javascript" src="$RT::WebPath/NoAuth/js/titlebox-state.js"></script>
+<script type="text/javascript"><!--
+ onLoadHook("loadTitleBoxStates()");
+END
+
+if ( $Focus ) {
+ $head .= qq{ onLoadHook("focusElementById('$Focus')");\n};
+}
+if ( $onload ) {
+ $head .= qq{ onLoadHook("$onload");\n};
+}
+
+$head .= '--></script>';
+
+$head .= $m->scomp( '/Elements/Callback', _CallbackName => 'Head', %ARGS );
+
+my $etc = '';
+$etc .= qq[ id="comp-$id"] if $id;
+
</%INIT>
<%ARGS>
diff --git a/rt/html/Elements/PageLayout b/rt/html/Elements/PageLayout
index 6897ede1a..3185662e1 100644
--- a/rt/html/Elements/PageLayout
+++ b/rt/html/Elements/PageLayout
@@ -45,34 +45,116 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
- <div id="topactions">
-% foreach my $action (reverse sort keys %{$topactions}) {
- <span class="topaction">
-% $m->out($topactions->{"$action"}->{'html'});
- </span>
-% }
- </div>
+% #false laziness w/menubar.html... shouldn't these just go in freeside.css?
+<style type="text/css">
+a.fsblackbutton {
+ background-color:#333333;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ /*font-weight:bold;*/
+ /*padding-left:12px;
+ padding-right:12px;*/
+ padding-left:4px;
+ padding-right:4px;
+ font-size:16px;
+ text-decoration:none;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff333333',EndColorStr='#ff666666')
+}
-%# End of div#quickbar from /Elements/Header
-</div>
+a.fsblackbuttonselected,
+a:link:hover.fsblackbutton,
+a:visited:hover.fsblackbutton {
+ background-color:#7e0079;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ /*font-weight:bold;*/
+ /*padding-left:12px;
+ padding-right:12px;*/
+ padding-left:4px;
+ padding-right:4px;
+ font-size:16px;
+ text-decoration:none;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff330033',EndColorStr='#ff7e0079')
+}
+
+a.fsdarkbutton {
+ background-color:#555555;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ /*font-weight:bold;*/
+ /*padding-left:12px;
+ padding-right:12px;*/
+ padding-left:4px;
+ padding-right:4px;
+ font-size:16px;
+ text-decoration:none;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff555555',EndColorStr='#ff666666')
+}
+
+a.fsdarkbuttonselected,
+a:link:hover.fsdarkbutton,
+a:visited:hover.fsdarkbutton {
+ background-color:#7e0079;
+ color: #ffffff;
+ border:1px solid;
+ border-top-color:#cccccc;
+ border-left-color:#cccccc;
+ border-right-color:#aaaaaa;
+ border-bottom-color:#aaaaaa;
+ /*font-weight:bold;*/
+ /*padding-left:12px;
+ padding-right:12px;*/
+ padding-left:4px;
+ padding-right:4px;
+ font-size:16px;
+ text-decoration:none;
+ overflow:visible;
+ filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#ff330033',EndColorStr='#ff7e0079')
+}
+</style>
+<% include('/elements/init_calendar.html') |n %>
+<table border=0 cellspacing=0 cellpadding=0 width="100%" height="100%">
+ <TR HEIGHT="100%">
+ <TD valign="top">
-% if ( $show_menu ) {
+% if (0) { ##FREESIDE MENUS INSTEAD## if ( $show_menu ) {
+%# if ( $show_menu ) {
<div id="nav">
<& /Elements/Menu, toptabs => $toptabs, current_toptab => $current_toptab &>
</div>
% }
-<div id="header">
- <h1><%$title%></h1>
+%#already shown <h1><%$title%></h1>
% my $sep = 0;
% my $postsep = 0;
% my $count = 0;
% my $class = { };
%
- <ul id="page-menu"<% (($actions && %$actions) || ($subactions && %$subactions)) && q[ class="actions-present"] | n %>>
- <div><div><div>
+
+ <TABLE BGCOLOR="#000000" BORDER=0 CELLSPACING=0 CELLPADDING=0>
+ <TR>
+
+%# <ul id="page-menu"<% (($actions && %$actions) || ($subactions && %$subactions)) && q[ class="actions-present"] | n %>>
+
% if ($page_tabs) {
+ <TD><IMG SRC="<%$fsurl%>images/gray-black-side.png" WIDTH=13 HEIGHT=25></TD>
+ <TD>
% foreach my $tab (sort keys %{$page_tabs}) {
% next if $tab =~ /^(?:current_toptab|this)$/;
% $count++;
@@ -100,22 +182,24 @@
% $class->{li} = join ' ', @li;
%
%
- <li<% $class->{li} ? qq[ class="$class->{li}"] : ''|n %>><% $count > 1 && !$postsep && "&#183; "|n%><a href="<%$RT::WebPath%>/<%$page_tabs->{$tab}->{'path'}%>"<%$class->{a}|n%><% $class->{a} ? ' name="focus"' : ''|n %>><% $page_tabs->{$tab}->{'title'} %></a></li>
-%
-% if ($sep) {
- <li class="separator">&#183;&#183;&#183;</li>
-% }
-% $postsep = $sep;
+
+%# <li<% $class->{li} ? qq[ class="$class->{li}"] : ''|n %>><% $count > 1 && !$postsep && "&#183; "|n%><a href="<%$RT::WebPath%>/<%$page_tabs->{$tab}->{'path'}%>"<%$class->{a}|n%><% $class->{a} ? ' name="focus"' : ''|n %>><% $page_tabs->{$tab}->{'title'} %></a></li>
+
+% $class->{a} = $path eq $current ? ' class="fsblackbuttonselected"' : ' class="fsblackbutton"';
+ <a href="<%$RT::WebPath%>/<%$page_tabs->{$tab}->{'path'}%>"<%$class->{a}|n%><% $class->{a} =~ 'selected' ? ' name="focus"' : ''|n %>><% $page_tabs->{$tab}->{'title'} %></a>
+
% }
-% } else {
-&nbsp;
+ </TD>
+%# } else {
+%#&nbsp;
% }
- </div></div></div>
- </ul>
% if (($actions && %$actions) || ($subactions && %$subactions)) {
- <ul id="actions-menu">
- <div><div><div>
+ <TD><IMG SRC="<%$fsurl%>images/<% $page_tabs ? 'black-gray' : 'gray-black' %>-side.png" WIDTH=13 HEIGHT=25></TD>
+% if ( $page_tabs ) {
+ <TD BGCOLOR="#333333">&nbsp;&nbsp;</TD>
+% }
+ <TD BGCOLOR="#333333">
% $sep = 0;
% $postsep = 0;
% $count = 0;
@@ -141,24 +225,30 @@
%
% $class->{li} = join ' ', @li;
%
- <li<% $class->{li} ? qq[ class="$class->{li}"] : ''|n %>><% $count > 1 && !$postsep && qq[<span class="bullet">&#183; </span>]|n%>
+
+%# <li<% $class->{li} ? qq[ class="$class->{li}"] : ''|n %>><% $count > 1 && !$postsep && qq[<span class="bullet">&#183; </span>]|n%>
+
+% #$class->{a} = $path eq $current ? ' class="fsblackbuttonselected"' : ' class="fsblackbutton"';
+% $type->{$action}->{class} ||= 'fsdarkbutton';
+
% if ($type->{"$action"}->{'html'}) {
<% $type->{"$action"}->{'html'} | n %>
% } else {
<a href="<%$RT::WebPath%>/<%$type->{$action}->{'path'}%>"<% $type->{$action}->{class} && ' class="'.$type->{$action}->{class}.'"' |n %><% $type->{$action}->{id} && ' id="'.$type->{$action}->{id}.'"' |n %>><%$type->{$action}->{'title'}%></a>
% }
- </li>
-% if ($sep) {
- <li class="separator">&#183;&#183;&#183;</li>
-% }
-% $postsep = $sep;
+
% }
% }
% }
- </div></div></div>
- </ul>
+
+ </TD>
+
% }
-</div>
+
+ <TD><IMG SRC="<%$fsurl%>images/black-gray-side.png" WIDTH=13 HEIGHT=25></TD>
+ </TR>
+</TABLE>
+
<div id="body">
<& /Elements/Callback, _CallbackName => 'BeforeBody', %ARGS &>
diff --git a/rt/html/Elements/QuickCreate b/rt/html/Elements/QuickCreate
index 5669a4544..83d3a0d38 100644
--- a/rt/html/Elements/QuickCreate
+++ b/rt/html/Elements/QuickCreate
@@ -47,11 +47,11 @@
%# END BPS TAGGED BLOCK }}}
<div class="quick-create">
<&| /Widgets/TitleBox, title => loc('Quick ticket creation') &>
-<form method="post" action="<%$RT::WebPath%>/index.html">
+<form method="post" action="<%$RT::WebPath%>/<% $RT::QuickCreateLong ? 'Ticket/Create.html' : 'index.html' %>">
<input type="hidden" class="hidden" name="QuickCreate" value="1" />
<table>
<tr><td>
-<&|/l&>Subject</&>:<br /><input size="15" name="Subject" />
+<&|/l&>Subject</&>:<br /><input size="30" name="Subject" />
</td><td>
<&|/l&>Queue</&>:<br /><& /Elements/SelectNewTicketQueue, Name => 'Queue', ShowNullOption => 0 &>
</td><td>
diff --git a/rt/html/Elements/SelectDate b/rt/html/Elements/SelectDate
index 5767074fb..d911cf713 100644
--- a/rt/html/Elements/SelectDate
+++ b/rt/html/Elements/SelectDate
@@ -45,10 +45,21 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
+%# in PageLayout instead, once <% include('/elements/init_calendar.html') |n %>
+<input type="text" id="<% $Name %>" name="<% $Name %>" value="<% $Default %>" size="<% $Size %>" />
+<IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $Name %>_date_button" STYLE="cursor: pointer" TITLE="Select date">
<script type="text/javascript"><!--
- onLoadHook('createCalendarLink("<% $Name %>");');
+Calendar.setup({
+ inputField: "<%$Name%>",
+% if ( defined($ShowTime) && $ShowTime ) {
+ ifFormat: "%Y-%m-%d %H:%M",
+ showsTime: true,
+% } else {
+ ifFormat: "%Y-%m-%d",
+% }
+ button: "<%$Name%>_date_button",
+});
--></script>
-<input type="text" id="<% $Name %>" name="<% $Name %>" value="<% $Default %>" size="<% $Size %>" />
<%init>
unless ((defined $Default) or
($current <= 0)) {
diff --git a/rt/html/Elements/ShadedBox b/rt/html/Elements/ShadedBox
deleted file mode 100644
index 36b9cae7c..000000000
--- a/rt/html/Elements/ShadedBox
+++ /dev/null
@@ -1,33 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<table>
- <tr>
- <td class="label"><%$title |n %>:</td>
- <td class="value"><%$content |n %></td>
- </tr>
-</table>
-<%ARGS>
-$title => undef
-$content => "&nbsp;"
-</%ARGS>
diff --git a/rt/html/Elements/ShadedInputRow b/rt/html/Elements/ShadedInputRow
deleted file mode 100644
index e9fb69e5f..000000000
--- a/rt/html/Elements/ShadedInputRow
+++ /dev/null
@@ -1,35 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<tr>
- <td class="label"><%$title |n %>:</td>
- <td class="value">
- <input name=<%$name%> value="<%$content|h%>" SIZE=<%$size%>>
- </td>
-</tr>
-<%ARGS>
-$title => undef
-$content => "&nbsp;"
-$name => undef
-$size => undef
-</%ARGS>
diff --git a/rt/html/Elements/ShadedRow b/rt/html/Elements/ShadedRow
deleted file mode 100644
index 8947fcd82..000000000
--- a/rt/html/Elements/ShadedRow
+++ /dev/null
@@ -1,31 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<tr>
- <td class="label"><%$title |n %>:</td>
- <td class="value"><%$content |n %></td>
-</tr>
-<%ARGS>
-$title => undef
-$content => "&nbsp;"
-</%ARGS>
diff --git a/rt/html/Elements/SimpleSearch b/rt/html/Elements/SimpleSearch
index 2876a2957..5f83da03e 100644
--- a/rt/html/Elements/SimpleSearch
+++ b/rt/html/Elements/SimpleSearch
@@ -45,7 +45,14 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<form action="<% $RT::WebPath %>/Search/Simple.html">
- <input size="12" name="q" autocomplete="off" accesskey="0" class="field" />
- <input type="submit" class="button" value="<&|/l&>Search</&>" />
+<form action="<% $RT::WebPath %>/Search/Simple.html" STYLE="margin:0">
+<SCRIPT TYPE="text/javascript">
+ function clearhint_search_ticket (what) {
+ if ( what.value == '(ticket # or subject string)' )
+ what.value = '';
+ }
+</SCRIPT>
+<input name="q" autocomplete="off" accesskey="0" class="field" VALUE="(ticket # or subject string)" onFocus="clearhint_search_ticket(this);" onClick="clearhint_search_ticket(this);" STYLE="text-align:right; font-family: Arial, Verdana, Helvetica, sans-serif;"><BR>
+<A HREF="<% $RT::WebPath %>/Search/Build.html" STYLE="color: #ffffff; font-size: 70%; font-weight:normal">Advanced</A>
+<input type="submit" class="fsblackbutton" value="<&|/l&>Search tickets</&>" onMouseOver="this.className='fsblackbuttonselected'; return true;" onMouseOut="this.className='fsblackbutton'; return true;" STYLE="font-size:70%;padding-left:2px;padding-right:2px">
</form>
diff --git a/rt/html/Elements/Tabs b/rt/html/Elements/Tabs
index 3b0f3da6d..7b3d80928 100644
--- a/rt/html/Elements/Tabs
+++ b/rt/html/Elements/Tabs
@@ -59,19 +59,18 @@
<a name="skipnav" id="skipnav" accesskey="8"></a>
<%INIT>
my $action;
-my $basetopactions = {
- A => { html => $m->scomp('/Elements/CreateTicket')
- },
- B => { html => $m->scomp('/Elements/SimpleSearch')
- }
- };
-my $basetabs = { A => { title => loc('Homepage'),
+my $basetabs = {
+ ' A'=> { title => 'Billing Main',
+ path => &RT::URI::freeside::FreesideURL(),
+ },
+ A => { #title => loc('Homepage'),
+ title => 'Ticketing Main',
path => '',
},
- Ab => { title => loc('Simple Search'),
+ Ab => { title => loc('Simple Ticket Search'),
path => 'Search/Simple.html'
},
- B => { title => loc('Tickets'),
+ B => { title => loc('Adv. Ticket Search'),
path => 'Search/Build.html'
},
C => { title => loc('Tools'),
@@ -99,9 +98,8 @@ if ($session{'CurrentUser'}->HasRight( Right => 'ModifySelf',
if (!defined $toptabs) {
$toptabs = $basetabs;
}
-if (!defined $topactions) {
- $topactions = $basetopactions;
-}
+
+ require RT::URI::freeside;
# Now let callbacks add their extra tabs
$m->comp('/Elements/Callback',
diff --git a/rt/html/Elements/TicketList b/rt/html/Elements/TicketList
index 81e265d93..7f715d921 100644
--- a/rt/html/Elements/TicketList
+++ b/rt/html/Elements/TicketList
@@ -65,7 +65,8 @@
% while (my $record = $Collection->Next) {
% $i++;
% # Every ten rows, flush the buffer and put something on the page.
-% $m->flush_buffer() unless ($i % 10);
+% # hun, this flushes things out out-of-order for me on "RT at a glance"...?
+% # $m->flush_buffer() unless ($i % 10);
<& /Elements/CollectionAsTable/Row, Format => \@Format, i => $i, record => $record, maxitems => $maxitems &>
% }
diff --git a/rt/html/Elements/ViewUser b/rt/html/Elements/ViewUser
deleted file mode 100644
index 657272496..000000000
--- a/rt/html/Elements/ViewUser
+++ /dev/null
@@ -1,51 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<& /Elements/TitleBoxStart,
- title => "<a class='inverse' href=\"$RT::WebPath/Search/Listing.html?LimitRequestorById=1&IdOfRequestor=".$User->id."\">".loc("Tickets from [_1]", $name)."</a>",
- titleright=> "<a class='inverse' href=\"$RT::WebPath/EditUserComments.html?id=".$User->id."\">".loc("Comments about [_1]", $name)."</a>" &>
-<TABLE WIDTH="100%">
-<tr>
-<td halign=left valign=top>
-%while (my $w=$tickets->Next) {
-<%$w->Id%>: <a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$w->id%>"><%$w->Subject%></a> (<%$w->Status%>)<BR>
-%}
-</td>
-<td align=right valign=top>
- <% ($User->Comments || loc("No comment entered about this user")) %>
-</tr>
-</table>
-<& /Elements/TitleBoxEnd &>
-
-<%ARGS>
-$User=>undef
-</%ARGS>
-
-<%INIT>
-my $name=$User->RealName || $User->EmailAddress;
-
-my $tickets = new RT::Tickets($session{'CurrentUser'});
-$tickets->LimitWatcher(TYPE => 'Requestor', VALUE => $User->EmailAddress);
-
-
-</%INIT>
diff --git a/rt/html/NoAuth/css/3.5-default/freeside.css b/rt/html/NoAuth/css/3.5-default/freeside.css
new file mode 100644
index 000000000..5e7e0a04a
--- /dev/null
+++ b/rt/html/NoAuth/css/3.5-default/freeside.css
@@ -0,0 +1,9 @@
+
+div.titlebox {
+ background: #d4d4d4;
+}
+
+div.titlebox-title {
+ background: #e8e8e8;
+}
+
diff --git a/rt/html/NoAuth/css/3.5-default/main.css b/rt/html/NoAuth/css/3.5-default/main.css
index 3a5fdfb5d..ed4a6989c 100644
--- a/rt/html/NoAuth/css/3.5-default/main.css
+++ b/rt/html/NoAuth/css/3.5-default/main.css
@@ -58,4 +58,5 @@
@import "nav.css";
@import "header.css";
@import "footer.css";
+@import "freeside.css";
diff --git a/rt/html/NoAuth/css/3.5-default/misc.css b/rt/html/NoAuth/css/3.5-default/misc.css
index 038e65def..981dea36d 100755
--- a/rt/html/NoAuth/css/3.5-default/misc.css
+++ b/rt/html/NoAuth/css/3.5-default/misc.css
@@ -46,10 +46,12 @@
%#
%# END BPS TAGGED BLOCK }}}
body {
- font-family: Verdana, sans-serif;
+ /* font-family: Verdana, sans-serif; */
+ font-family: Arial, Verdana, Helvetica, sans-serif;
font-size: 76%;
margin: 0;
- background-color: white;
+ /* background-color: white; */
+ background-color: #e8e8e8;
}
.hide, .hidden { display: none !important; }
@@ -73,9 +75,11 @@ body {
.evenline { background-color: white; }
.oddline { background-color: #ddd; }
+/*
td {
padding: 0.1em 0.5em 0.1em 0.5em;
}
+*/
.clear { clear: both; }
diff --git a/rt/html/NoAuth/css/3.5-default/titlebox.css b/rt/html/NoAuth/css/3.5-default/titlebox.css
index 3bd4e97cf..82961bb38 100644
--- a/rt/html/NoAuth/css/3.5-default/titlebox.css
+++ b/rt/html/NoAuth/css/3.5-default/titlebox.css
@@ -51,7 +51,8 @@
.titlebox .titlebox-content {
margin-top: -1px;
- padding: 1em 2em 0.5em 2em;
+ padding: 0.25em .50em 0.125em .50em;
+ /*padding: 1em 2em 0.5em 2em;*/
margin: 0;
/*margin: 1em 2em 0.5em 2em;*/
}
diff --git a/rt/html/NoAuth/css/3.5-default/transactions.css b/rt/html/NoAuth/css/3.5-default/transactions.css
index fdf8ea824..fee5c52f2 100755
--- a/rt/html/NoAuth/css/3.5-default/transactions.css
+++ b/rt/html/NoAuth/css/3.5-default/transactions.css
@@ -57,6 +57,10 @@
.ticket-transaction.even {
background: #eee;
}
+.ticket-transaction.odd {
+ background: #fff;
+}
+
.ticket-transaction .date {
font-size: 0.9em;
diff --git a/rt/html/NoAuth/images/back_home.gif b/rt/html/NoAuth/images/back_home.gif
deleted file mode 100644
index 40b19c153..000000000
--- a/rt/html/NoAuth/images/back_home.gif
+++ /dev/null
Binary files differ
diff --git a/rt/html/NoAuth/images/css/cb.gif b/rt/html/NoAuth/images/css/cb.gif
index 53bb2aec2..49a4a97b6 100644
--- a/rt/html/NoAuth/images/css/cb.gif
+++ b/rt/html/NoAuth/images/css/cb.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/cbr.gif b/rt/html/NoAuth/images/css/cbr.gif
index 754cee19b..eeb7ff444 100644
--- a/rt/html/NoAuth/images/css/cbr.gif
+++ b/rt/html/NoAuth/images/css/cbr.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/ct.gif b/rt/html/NoAuth/images/css/ct.gif
index d16a5c57f..d2ae8d8ae 100644
--- a/rt/html/NoAuth/images/css/ct.gif
+++ b/rt/html/NoAuth/images/css/ct.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/css/ctr.gif b/rt/html/NoAuth/images/css/ctr.gif
index 9754e1567..d17e647c0 100644
--- a/rt/html/NoAuth/images/css/ctr.gif
+++ b/rt/html/NoAuth/images/css/ctr.gif
Binary files differ
diff --git a/rt/html/NoAuth/images/head_requestracker.gif b/rt/html/NoAuth/images/head_requestracker.gif
deleted file mode 100644
index 73315e918..000000000
--- a/rt/html/NoAuth/images/head_requestracker.gif
+++ /dev/null
Binary files differ
diff --git a/rt/html/NoAuth/images/rt.jpg b/rt/html/NoAuth/images/rt.jpg
deleted file mode 100644
index a137a932b..000000000
--- a/rt/html/NoAuth/images/rt.jpg
+++ /dev/null
Binary files differ
diff --git a/rt/html/NoAuth/images/small-logo.png b/rt/html/NoAuth/images/small-logo.png
new file mode 100644
index 000000000..1e415e6d8
--- /dev/null
+++ b/rt/html/NoAuth/images/small-logo.png
Binary files differ
diff --git a/rt/html/NoAuth/images/space.gif b/rt/html/NoAuth/images/space.gif
deleted file mode 100644
index 1d11fa9ad..000000000
--- a/rt/html/NoAuth/images/space.gif
+++ /dev/null
Binary files differ
diff --git a/rt/html/NoAuth/images/spacer.gif b/rt/html/NoAuth/images/spacer.gif
deleted file mode 100644
index 5bfd67a2d..000000000
--- a/rt/html/NoAuth/images/spacer.gif
+++ /dev/null
Binary files differ
diff --git a/rt/html/NoAuth/images/squares_blue.gif b/rt/html/NoAuth/images/squares_blue.gif
deleted file mode 100644
index a28da5ce1..000000000
--- a/rt/html/NoAuth/images/squares_blue.gif
+++ /dev/null
Binary files differ
diff --git a/rt/html/NoAuth/printrt.css b/rt/html/NoAuth/printrt.css
deleted file mode 100644
index 72e7e8b7e..000000000
--- a/rt/html/NoAuth/printrt.css
+++ /dev/null
@@ -1,77 +0,0 @@
-%# {{{ BEGIN BPS TAGGED BLOCK
-%#
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# }}} END BPS TAGGED BLOCK
-%#
-%# Special stylesheet for printing tickets
-%# Koos van den Hout koos@cs.uu.nl 2005-11-21
-%#
-
-SPAN.nav { display: none !important; }
-.nav2 { display: none !important; }
-.nav { display: none !important; }
-.topnav { display: none !important; }
-.blue { display: none !important; }
-.darkblue { display: none !important; }
-.blueright { display: none !important; }
-.currentnav { display: none !important; }
-th.titlebox { border-top: none; border-bottom: none; }
-th.titleboxright { display:none !important; border-top: none; border-bottom: none; }
-.titlebox { border-top: none; border-bottom: none; }
-
-div.downloadattachment, div.downloadcontenttype {
- display: none !important;
-}
-
-
-a[href$="Respond"], a[href$="Comment"], a[href*="ShowEmailRecord"] {
- display: none !important;
-}
-
-
-%# Provide a callback for adding/modifying the style sheet.
-%# http://www.w3.org/TR/REC-CSS1 - section 3.2, says:
-%# "latter specified rule wins"
-<& /Elements/Callback &>
-<%flags>
-inherit => undef
-</%flags>
-<%init>
-$r->content_type('text/css');
-$r->headers_out->{'Expires'} = '+30m';
-</%init>
diff --git a/rt/html/NoAuth/webrt.css b/rt/html/NoAuth/webrt.css
deleted file mode 100644
index 7fa2f83f8..000000000
--- a/rt/html/NoAuth/webrt.css
+++ /dev/null
@@ -1,628 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
-%# <jesse@bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-SPAN.nav { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 12px;
- color: #FFFFFF;
- text-decoration: none;
- white-space: nowrap}
-.nav2 { font-size: 10px;
- white-space: nowrap}
-.nav { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 13px;
- font-weight: normal;
- color: #FFFFFF;
- text-decoration: none;
- white-space: nowrap}
-.currentnav { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 13px;
- font-weight: bold;
- color: #FFFF66;
- text-decoration: none;
- white-space: nowrap}
-.topnav { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 16px;
- font-weight: normal;
- color: #FFFFFF;
- text-decoration: none;
- white-space: nowrap}
-
-%# .topnav is the original RT class for the sidebar navigation tabs.
-%# Font-sizing by level depth was originally hard-coded into Elements/Menu.
-%# This modification sets a different class name for each level, allowing
-%# style sheet control over the formats.
-
-a.topnav-0 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 16px;
- font-weight: normal;
- color: #FFFFFF;
- text-decoration: none;
- white-space: nowrap}
-a.topnav-1 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 14px;
- font-weight: normal;
- color: #FFFFFF;
- text-decoration: none;
- white-space: nowrap}
-a.topnav-2 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 12px;
- font-weight: normal;
- color: #FFFFFF;
- text-decoration: none;
- white-space: nowrap}
-a.topnav-3 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- font-weight: normal;
- color: #FFFFFF;
- text-decoration: none;
- white-space: nowrap}
-a.topnav-4 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- font-weight: normal;
- color: #FFFFFF;
- text-decoration: none;
- white-space: nowrap}
-a.topnav-5 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- font-weight: normal;
- color: #FFFFFF;
- text-decoration: none;
- white-space: nowrap}
-li.topnav-0-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.topnav-1-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.topnav-2-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.topnav-3-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.topnav-4-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.topnav-5-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.topnav-0-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-li.topnav-1-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-li.topnav-2-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-li.topnav-3-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-li.topnav-4-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-li.topnav-5-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-
-.currenttopnav { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 16px;
- font-weight: bold;
- color: #FFFF66;
- text-decoration: none;
- white-space: nowrap}
-
-%# .currenttopnav is the original RT class for the sidebar navigation tabs.
-%# Font-sizing by level depth was originally hard-coded into Elements/Menu.
-%# This modification sets a different class name for each level, allowing
-%# style sheet control over the formats
-
-a.currenttopnav-0 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 16px;
- font-weight: bold;
- color: #FFFF66;
- text-decoration: none;
- white-space: nowrap}
-a.currenttopnav-1 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 14px;
- font-weight: bold;
- color: #FFFF66;
- text-decoration: none;
- white-space: nowrap}
-a.currenttopnav-2 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 12px;
- font-weight: normal;
- color: #FFFF66;
- text-decoration: none;
- white-space: nowrap}
-a.currenttopnav-3 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- font-weight: normal;
- color: #FFFF66;
- text-decoration: none;
- white-space: nowrap}
-a.currenttopnav-4 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- font-weight: normal;
- color: #FFFF66;
- text-decoration: none;
- white-space: nowrap}
-a.currenttopnav-5 { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- font-weight: normal;
- color: #FFFF66;
- text-decoration: none;
- white-space: nowrap}
-li.currenttopnav-0-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.currenttopnav-1-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.currenttopnav-2-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.currenttopnav-3-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.currenttopnav-4-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.currenttopnav-5-minor {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-li.currenttopnav-0-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-li.currenttopnav-1-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-li.currenttopnav-2-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-li.currenttopnav-3-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-li.currenttopnav-4-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-li.currenttopnav-5-major {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-
-.topactions { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 10px;
- color: #FFFFFF;
- text-decoration: none;
- white-space: nowrap}
-.subnav { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- font-weight: normal;
- color: #FFFFFF;
- text-decoration: none;
- white-space: nowrap}
-.currentsubnav { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- font-weight: bold;
- color: #FFFF66;
- text-decoration: none;
- white-space: nowrap}
-.error { background-color: #ff0000;
- background-position: left top;
- vertical-align: top;
- text-align: left;
- }
-.oldblue { background-color: #0066CC;
- background-position: left top;
- vertical-align: top;
- text-align: left;
- }
-.blue { background-color: #4682B4;
- background-position: left top;
- vertical-align: top;
- text-align: left;
- }
-%# Actually the "topactions" section
-.blueright { background-color: #4682B4;
- background-position: left top;
- vertical-align: top;
- text-align: right;
- padding-right: 1em;
- }
-.olddarkblue { background-color: #003399;
- background-position: left top;
- vertical-align: top;
- text-align: left;
- }
-.darkblue { background-color: #000080;
- background-position: left top;
- vertical-align: top;
- text-align: left;
- }
-.darkblueright { background-color: #000080;
- background-position: left top;
- vertical-align: top;
- text-align: right;
- }
-.overdue {
- color: red;
-}
-
-div.messagebody {
- padding: 2em;
-
-}
-
-
-div.downloadattachment {
- font-size: 10px;
- text-align: right;
-
-}
-
-
-td { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- background-position: left top;
- }
-.black { background-color: #000000;
- background-position: left top;
- }
-span.rtname { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 18px;
- font-weight: normal;
- color: #ffffff}
-span.title { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 20px;
- font-weight: bold;
- color: #ffffff}
-.header { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 12px;
- font-weight: bold;
- color: #0066CC}
-.subheader { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- font-weight: bold;
- color: #0066CC }
-.value { font-weight: bold; }
-.entry { font-weight: normal; }
-.label { font-weight: normal;
- text-align: right; }
-.labeltop { font-weight: normal;
- text-align: right;
- vertical-align: top }
-.productnav { font-family: Verdana, Arial, Helvetica, sans-serif;
- font-size: 11px;
- color: #000000;
- text-align: center;
- vertical-align: middle;
- text-decoration: none}
-.rtblue { background-color: #3399FF;
- margin-top: 0.2em;
- background-position: left top;
- vertical-align: top }
-
-
-.currenttab { margin: 0.2em; background: #336699; }
-.othertab { margin: 0.2em; background: #efefef; }
-.oddline { background-color : #ccccee; }
-
-UL.topnav LI :focus { text-decoration: underline; }
-
-TD.mainbody {
- padding-top: 0.5em;
- padding-left: 1em;
- padding-right: 1em;
- margin-left: 1em;
- margin-right: 1em;
-}
-
-td.boxcontainer + td.boxcontainer {
- margin-left: 1em;
- padding-left: 1em;
- border-collapse: collapse;
-}
-
-th.ticketheader { font-size: 80%;
- font-weight: bold;
- color: #336699;
- background: #cccccc;
-}
-
-th.titlebox {
- text-align: left;
- padding-left: 0.5em;
- padding-right: 0.5em;
- margin-left: 0.5em;
- margin-right: 0.5em;
- border-top: solid black 1px;
- border-bottom: solid black 1px;
-}
-th.titleboxright {
- text-align: right;
- padding-left: 0.5em;
- padding-right: 0.5em;
- margin-left: 0.5em;
- margin-right: 0.5em;
- border-top: solid black 1px;
- border-bottom: solid black 1px;
-}
-
-TD.titlebox {
- padding-left: 1em;
- padding-right: 1em;
- padding-top: 1em;
- padding-bottom: 1em;
-}
-
-SPAN.message {
- font-size: 100%;
- font-family: Verdana, Arial, Helvetica, sans-serif;
-}
-
-
-BODY {
- color: #000;
- background: #FFFFFF;
- font-family: Verdana, Arial, Helvetica, sans-serif;
- margin-top: 0px;
- margin-bottom: 0px;
- margin-left: 0px;
- margin-right: 0px;
- border-top: 0px;
- border-bottom: 0px;
- border-left: 0px;
- border-right: 0px;
-}
-
-
-TR.oddline {
- background-color : #ffffff;
-}
-
-TR.evenline {
- background-color : #ccccee;
-}
-
-H1, H2, H3 {
- margin-top: 0.2em;
- color: #336699;
- font-family: Verdana, Arial, Helvetica, sans-serif;
-
- clear: both;
-}
-
-
-DIV.endmatter { margin-left: -7% }
-.bpscredits {margin-top: 1em;
- text-align: right;
- color: #666666;
- }
-
-
-A { font-weight: bold; color: #000000;
- }
-
-.currenttab { color: #ffffff;}
-.othertab { color: #336699; }
-
-.inverse { color: #ffffff; }
-
-
-
-A:link IMG, A:visited IMG { border-style: none }
-a:focus {text-decoration: underline }
-A IMG { color: white } /* The only way to hide the border in NS 4.x */
-
-a:link { text-decoration: none}
-a:visited { text-decoration: none}
-a:hover { text-decoration: underline}
-/* a:focus { background-color: #ccccee } */
-
-.hide {
- display: none;
- color: white;
-}
-
-SPAN.date { font-size: 0.8em }
-
-span.title { font-size: 1.6em;
- vertical-align: middle;
- color: #ffffff;}
-span.productname { font-size: 2em;
- color: #0066cc;}
-SPAN.titleboxtitle, SPAN.titleboxclose {
- font-size: 80%;
- color: #ffffff;
- vertical-align: middle;
- text-align: left;
- }
-SPAN.titleboxtitle a {
- color: #ffffff;
-}
-SPAN.titleboxtitle a:after {
- content: "...";
-}
-
-SPAN.titleboxright {
- font-size: 0.8em;
- color: #ffffff;
- vertical-align: middle;
- text-align: right;
- }
-
-SPAN.attribution {
- font-weight: bold;
-}
-
-SPAN.label { font-size: 0.8em;
-}
-
-DIV.page-stats { font-size: 0.8em;
- color: #cccccc;
- text-align: right;
- }
-
-
-BLOCKQUOTE {
- font-style: italic;
-}
-
-.emphasized {
- font-weight: bold
-}
-
-
-.oddline {
- background-color : #ccccee;
-}
-
-ul.topnav {
- list-style: none;
- margin-left: 0;
- margin-right: 0.25em;
- padding-left: 0.25em;
- padding-bottom: 0;
- padding-top:0;
- margin-top: 0;
- margin-bottom:0;
-}
-
-.menu-major-separator {
- border-bottom: solid white 1px;
- padding-top: .25em;
- padding-bottom: .5em;
-}
-
-.menu-minor-separator {
- border-top: solid #999999 1px;
- padding-top: .1em;
- margin-top: .5em;
-}
-
-TH.collection-as-table { text-align: center;
- font-size: 0.8em;
- padding-left: .5em;
- padding-right: .5em;
- color: #333333;
- background-color: #cccccc;
- white-space: nowrap;
- }
-
-TD.collection-as-table { text-align: left;
- padding-left: .5em;
- padding-right: .5em;
- }
-
-textarea.signature {
- width: 100%;
-}
-textarea.comments {
- width: 100%;
-}
-
-textarea.messagebox {
- width: 100%;
-}
-
-%# Provide a callback for adding/modifying the style sheet.
-%# http://www.w3.org/TR/REC-CSS1 - section 3.2, says:
-%# "latter specified rule wins"
-<& /Elements/Callback &>
-<%flags>
-inherit => undef
-</%flags>
-<%init>
-$r->content_type('text/css');
-#$r->headers_out->{'Expires'} = '+30m';
-</%init>
diff --git a/rt/html/Prefs/SearchOptions.html b/rt/html/Prefs/SearchOptions.html
index 655d6ec39..06f32ba7e 100644
--- a/rt/html/Prefs/SearchOptions.html
+++ b/rt/html/Prefs/SearchOptions.html
@@ -46,7 +46,7 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<& /Elements/Header, Title => loc("Search Preferences") &>
+<& /Elements/Header, Title => loc("Ticketing Search Preferences") &>
<& /User/Elements/Tabs,
current_tab => "Prefs/SearchOptions.html",
Title => loc("Search Preferences")
diff --git a/rt/html/RTx/Statistics/CallsMultiQueue/Elements/Chart b/rt/html/RTx/Statistics/CallsMultiQueue/Elements/Chart
new file mode 100755
index 000000000..02a183b2c
--- /dev/null
+++ b/rt/html/RTx/Statistics/CallsMultiQueue/Elements/Chart
@@ -0,0 +1,39 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+</%perl>
+<em><&|/l, $#data+1&>[_1] Plot Elements</&></em><p>
+% foreach my $value (@data) {
+<% $value %><p>
+% }
+<em><&|/l&>x_labels</&>:</em><p>
+<% $ARGS{x_labels} %>
+<p>
+<em><&|/l&>legend</&>:</em><p>
+<% $ARGS{set_legend} %>
+<p>
+<em><&|/l, (keys %ARGS) - 2&>[_1] data sets</&>:</em><p>
+
+% for (1..(scalar keys %ARGS)-2) {
+<% $_ %> <% $ARGS{"data$_"} %><p>
+% }
+
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+ x_label => 'Day of Week',
+ y_label => 'Tickets per day');
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+my $format = $graph->export_format;
+push @data, [split /,/ , $ARGS{x_labels}];
+for (1..((scalar keys %ARGS)-2)) {
+ push @data, [split /,/ , $ARGS{"data".$_}];
+}
+
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/CallsMultiQueue/index.html b/rt/html/RTx/Statistics/CallsMultiQueue/index.html
new file mode 100755
index 000000000..abf8aa74a
--- /dev/null
+++ b/rt/html/RTx/Statistics/CallsMultiQueue/index.html
@@ -0,0 +1,330 @@
+<& /Elements/Header, Title => loc('Tickets per day in Multiple queues') &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc('Tickets per day in Multiple Queues by status') &>
+
+<h3>Description</h3>
+<p>This chart shows details of tickets per day by their status. You can select multiple queues to display at the same time, but only one status. You can chose any of the defined status values.
+There is also the option to display all available queues at the same time.
+The default display shows tickets resolved in your default queue (General unless altered locally).
+The line chart below shows the same information in a graphical form.
+
+<br />
+
+<form method="POST" action="index.html">
+
+%# Build Legend
+% my @legend;
+% for (sort keys %queues_to_show) {
+% push @legend, $_;
+% }
+
+%my $title = "Tickets with Status $status in " . join(', ', @queues) . ", per day from " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[0]) . " through " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$#dates-1]);
+
+<& /Elements/TitleBoxStart, title => $title, title_href => "/RTx/Statistics/OpenStalled/index.html?$QueryString"&>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH="100%">
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header,
+ Format => \@RowFormat,
+ FormatString => $RowFormat,
+ AllowSorting => $AllowSorting,
+ Order => $Order,
+ Query => undef,
+ Rows => $Rows,
+ Page => $Page,
+ OrderBy => $OrderBy ,
+ BaseURL => $BaseURL,
+ maxitems => $maxitems &>
+% }
+% my $line = 0;
+% LINE: for my $d (0..$#dates) {
+% if ($d == $#dates ){
+% next LINE;
+% }
+% $line++;
+% my $x = 1;
+% $values{Statistics_Date} = Statistics::FormatDate($dateformat, $dates[$d]);
+% my $row_total=0;
+% foreach my $q (sort keys %queues_to_show) {
+% my $tix = new RT::Tickets($session{'CurrentUser'});
+% if ($status eq "resolved") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitResolved(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitResolved(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% elsif ($status eq "new") {
+% $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% elsif ($status eq "deleted") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% elsif ($status eq "stalled") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% elsif ($status eq "open") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% elsif ($status eq "rejected") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% }
+% $tix->LimitQueue (VALUE => $q);
+% $values{$q} = $tix->Count;
+% $row_total += $tix->Count;
+% $data[$x++][$d] = $tix->Count;
+% }
+% $values{Statistics_Totals} = $row_total;
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@RowFormat, i => $line, record => $record, maxitems => $maxitems &>
+% }
+</table>
+<& /Elements/TitleBoxEnd&>
+
+<hr>
+
+<BR />
+<BR />
+
+<!-- <td>Show:</td>
+ <td COLSPAN=2><SELECT NAME="status">
+% for (qw(resolved new deleted stalled rejected open)) {
+ <OPTION VALUE="<% $_ %>" <% $_ eq $status && "SELECTED" %>>
+ <% loc($_) %></OPTION>
+% }
+--!>
+
+<%perl>
+# Create the graph URL
+my $url = 'Elements/Chart?x_labels=';
+#$url .= join ",", @{ shift @data } . "&";
+for (0..$max) {
+ $url .= $m->interp->apply_escapes($data[0][$_],'u') . ",";
+}
+chop $url;
+$url .= "&";
+shift @data;
+$url .= 'set_legend='.(join ",", @legend)."&";
+for (0..$#data) {
+ $url .= "data".(1+$_)."=". (join ",", @{$data[$_]})."&";
+}
+chop $url;
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox,
+ Title => "Change Status, Queues or Dates",
+ ShowDates => 1, sMonth => \$sMonth, sDay => \$sDay, sYear => \$sYear,
+ eMonth => \$eMonth, eDay => \$eDay, eYear => \$eYear,
+ weekends => $weekends,
+ ShowMultiQueues => 1, queues_ref => \@queues,
+ ShowStatus => 1, Status => $status
+ &>
+
+</form>
+
+<a href="<%$RT::WebPath%>/RTx/Statistics/CallsMultiQueue/index.html?<% $QueryString %>"><&|/l&>Bookmarkable link</&></a>
+%# | <a href="<%$RT::WebPath%>/RTx/Statistics/CallsMultiQueue/Results.tsv?<%$QueryString%>"><&|/l&>spreadsheet</&></a>
+<BR>
+<BR>
+
+<%ARGS>
+$status => $Statistics::MultiQueueStatus
+$max => $Statistics::MultiQueueMaxRows
+@queues => @Statistics::MultiQueueQueueList
+$weekends => $Statistics::PerDayWeekends;
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$dateformat => $Statistics::MultiQueueDateFormat
+$currentMonth=>undef
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+$AddAllCheck => undef
+</%ARGS>
+
+<%INIT>
+
+use RTx::Statistics;
+use Time::Local;
+my $n = 0;
+my @data = ([]);
+my @dates;
+my @msgs;
+my $selected;
+my $diff;
+my %queues_to_show;
+my $secsPerDay=86400;
+my $sEpoch;
+my $eEpoch;
+my $QueryString;
+my $maxitems;
+my $RowFormat;
+my $BoldRowFormat;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+Statistics::DebugClear();
+Statistics::DebugLog("CallsQueueDay/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+ Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+
+ # Handle the Add All Checkbox
+ if($AddAllCheck eq "on") {
+ $AddAllCheck = undef;
+ undef (@queues);
+ my $q=new RT::Queues($session{'CurrentUser'});
+ $q->UnLimit;
+ while (my $queue=$q->Next) {
+ next if !$queue->CurrentUserHasRight('SeeQueue');
+ push @queues, $queue->Name;
+ }
+ }
+
+ # If the user has the right to see the queue, put it into the map
+ for my $q (@queues) {
+ my $Queueobj = new RT::Queue($session{'CurrentUser'});
+ $Queueobj->Load($q);
+ next if !$Queueobj->CurrentUserHasRight('SeeQueue');
+ $queues_to_show{$q} = 1;
+ }
+
+ $maxitems = (scalar @queues) + 2;
+
+ # Build the format strings
+ $RowFormat = "'__Statistics_Date__'";
+ $BoldRowFormat = "'<B>__Statistics_Date__</B>'";
+ for my $q (@queues) {
+ $RowFormat .= ",'__Statistics_Dynamic__/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+ $BoldRowFormat .= ",'<B>__Statistics_Dynamic__</B>/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+ }
+ $RowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+ $BoldRowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+ # Parse the formats into structures.
+ my (@RowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $RowFormat);
+ my (@BoldRowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldRowFormat);
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+ $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+ $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+ $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+ # This case happens when the page is first loaded
+ my @local = localtime(time);
+ ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+ $eYear += 1900;
+ $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+ # We have an end, but not a start, or, overlapping.
+
+ # if $currentMonth is set, just set the day to 1
+ if($currentMonth) {
+ # set start vars from end, but with day set to 1
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+ $sDay=1;
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+ } else {
+ # If the user has specified how many days back to go, use that,
+ # If not, set start to configured default period before end
+ if(defined $days) {
+ $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+ } else {
+ $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+ }
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+ }
+ $sYear += 1900;
+}
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+$diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+Statistics::DebugLog("Setting diff=$diff\n");
+
+Statistics::DebugLog("sEpoch=$sEpoch, components=" . join(',', localtime($sEpoch)) . "\n");
+Statistics::DebugLog("eEpoch=$eEpoch, components=" . join(',', localtime($eEpoch)) . "\n");
+
+# Build the new query string
+$QueryString = "queues=" . join("&queues=", @queues);
+$QueryString .= "&sDay=$sDay&sMonth=$sMonth&sYear=$sYear&eDay=$eDay&eMonth=$eMonth&eYear=$eYear&weekends=$weekends";
+
+
+
+
+# Set up the end date to be midnight(morning) of the date after the one the user wanted.
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+$n = 0;
+until ($#dates == $diff) {
+ my $date = new RT::Date($session{CurrentUser});
+ $date->Set(Value=>$endRange - $n, Format => 'unix');
+ # Note: we used to adjust the time to local midnight, but
+ # none of the other date entry fields in RT seem to adjust, so we've stopped.
+ #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+ $n+= $Statistics::secsPerDay;
+ # If we aren't showing weekends and this is one, decrement the number
+ # of days to show and skip to the next date.
+ if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+ unshift @dates, $date;
+Statistics::DebugLog("pushing date " . Statistics::FormatDate("%c", $date) . "\n");
+ unshift @{ $data[0] }, Statistics::FormatDate($Statistics::PerDayLabelDateFormat, $date);
+}
+
+# We put an extra day into the lists to cover up till midnight of the next day,
+# But we don't want that to appear in the labels, so pop it off.
+pop( @{ $data[0] } );
+
+my $queue = new RT::Queues($session{CurrentUser});
+$queue->UnLimit;
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($queue);
+</%INIT>
diff --git a/rt/html/RTx/Statistics/CallsQueueDay/Elements/Chart b/rt/html/RTx/Statistics/CallsQueueDay/Elements/Chart
new file mode 100755
index 000000000..9a3a50574
--- /dev/null
+++ b/rt/html/RTx/Statistics/CallsQueueDay/Elements/Chart
@@ -0,0 +1,29 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+ x_label => 'Day of Week',
+ y_label => 'Tickets per Day',
+ x_labels_vertical => 1,
+ );
+my $format = $graph->export_format;
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/CallsQueueDay/Results.tsv b/rt/html/RTx/Statistics/CallsQueueDay/Results.tsv
new file mode 100644
index 000000000..23f0c699c
--- /dev/null
+++ b/rt/html/RTx/Statistics/CallsQueueDay/Results.tsv
@@ -0,0 +1,191 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Queue => undef
+$weekends => $Statistics::PerDayWeekends;
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$currentMonth=>undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+my @dates;
+my $n = 0;
+my %Totals;
+my $now = new RT::Date($session{CurrentUser});
+my $sEpoch;
+my $eEpoch;
+
+if (!defined $Queue) {
+ $Queue = $Statistics::PerDayQueue;
+}
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+ $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+ $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+ $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+ # This case happens when the page is first loaded
+ my @local = localtime(time);
+ ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+ $eYear += 1900;
+ $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+ # We have an end, but not a start, or, overlapping.
+
+ # if $currentMonth is set, just set the day to 1
+ if($currentMonth) {
+ # set start vars from end, but with day set to 1
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+ $sDay=1;
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+ } else {
+ # If the user has specified how many days back to go, use that,
+ # If not, set start to configured default period before end
+ if(defined $days) {
+ $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+ } else {
+ $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+ }
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+ }
+ $sYear += 1900;
+}
+
+# set content type
+$r->content_type('application/vnd.ms-excel');
+
+# Put out some data about the generation of this file
+$m->out("Tickets per day for Queue:\t" . $Queue . "\tGenerated at:\t" . Statistics::FormatDate("%x %X", $now). "\n\n");
+
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+my $diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+
+# Build array of dates
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($Queue);
+until ($#dates == $diff) {
+ my $date = new RT::Date($session{CurrentUser});
+ $date->Set(Value=>$endRange - $n, Format => 'unix');
+ # Note: we used to adjust the time to local midnight, but
+ # none of the other date entry fields in RT seem to adjust, so we've stopped.
+ #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+ $n+= $Statistics::secsPerDay;
+ # If we aren't showing weekends and this is one, decrement the number
+ # of days to show and skip to the next date.
+ if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+ unshift @dates, $date;
+}
+
+# Output header row
+$m->out("Date\tcreate\tresolved\tdeleted\n");
+
+
+LINE: for my $d (0..$#dates) {
+ if ($d == $#dates){
+ next LINE;
+ }
+ my $x = 1;
+ # Output the date for this row
+ $m->out(Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$d]));
+
+ # output the 3 columns for this row
+ for my $status (qw(created resolved deleted)) {
+ my $tix = new RT::Tickets($session{'CurrentUser'});
+ if ($status eq "created") {
+ $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+ if ($dates[$d+1]) {
+ $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+ }
+ } elsif ($status eq "resolved") {
+ $tix->LimitStatus(VALUE => $status);
+ $tix->LimitResolved(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+ if ($dates[$d+1]) {
+ $tix->LimitResolved(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+ }
+ } elsif ($status eq "deleted") {
+ $tix->LimitStatus(VALUE => $status);
+ $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+ if ($dates[$d+1]) {
+ $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+ }
+ }
+ $tix->LimitQueue (VALUE => $Queue);
+ $m->out( "\t" . $tix->Count );
+ $Totals{$status} += $tix->Count;
+ }
+ $m->out("\n");
+}
+
+# Output the totals
+$m->out("Totals\t$Totals{created}\t$Totals{resolved}\t$Totals{deleted}\n");
+
+$m->abort();
+</%INIT>
diff --git a/rt/html/RTx/Statistics/CallsQueueDay/index.html b/rt/html/RTx/Statistics/CallsQueueDay/index.html
new file mode 100755
index 000000000..06fc484d1
--- /dev/null
+++ b/rt/html/RTx/Statistics/CallsQueueDay/index.html
@@ -0,0 +1,275 @@
+<& /Elements/Header, Title => loc("Tickets per day in Queue:" . $QueueObj->Name()) &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc("Tickets by status per day in Queue:" . $QueueObj->Name()) &>
+
+<h3>Description</h3>
+<p>This page displays details about tickets in the selected queue over the date range chosen. It shows how many tickets were created on
+each day in the chosen range, and how many of those were either Resolved or Deleted.</p>
+<p>To always show the current month to date, bookmark this <a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/index.html?currentMonth=1">link</a>, or
+for a spreadsheet, use this <a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/Results.tsv?currentMonth=1">link</a>.</p>
+
+<form method="POST" action="index.html">
+
+% Statistics::DebugLog("queue name=" . $QueueObj->Name() . "\n");
+
+%my $title = "Ticket counts in " . $QueueObj->Name() . " by status per day from " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[0]) . " through " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$#dates-1]);
+<&|/Elements/TitleBox,
+ title => $title,
+ title_href => "/RTx/Statistics/CallsQueueDay/index.html?$QueryString" &>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH=100%>
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header,
+ Format => \@Format,
+ FormatString => $Format,
+ AllowSorting => $AllowSorting,
+ Order => $Order,
+ Query => undef,
+ Rows => $Rows,
+ Page => $Page,
+ OrderBy => $OrderBy ,
+ BaseURL => $BaseURL,
+ maxitems => $maxitems &>
+% }
+% my $line = 1;
+% LINE: for my $d (0..$#dates) {
+% if ($d == $#dates){
+% next LINE;
+% }
+% my $x = 1;
+% $values{Statistics_Date} = Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$d]);
+%# NOTE need to handle all status values here....
+% for my $status (qw(created resolved deleted)) {
+% my $tix = new RT::Tickets($session{'CurrentUser'});
+% $tix->LimitQueue (VALUE => $Queue);
+% if ($status eq "created") {
+% $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% $values{Statistics_Created_Count} = $tix->Count;
+% $Totals{Statistics_Created_Count} += $tix->Count;
+% }
+% elsif ($status eq "resolved") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitResolved(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitResolved(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% $values{Statistics_Resolved_Count} = $tix->Count;
+% $Totals{Statistics_Resolved_Count} += $tix->Count;
+% }
+% elsif ($status eq "deleted") {
+% $tix->LimitStatus(VALUE => $status);
+% $tix->LimitLastUpdated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitLastUpdated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% $values{Statistics_Deleted_Count} = $tix->Count;
+% $Totals{Statistics_Deleted_Count} += $tix->Count;
+% }
+% $data[$x++][$d] = $tix->Count;
+% }
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@Format, i => $line, record => $record, maxitems => $maxitems &>
+% $line++;
+% }
+% $values {Statistics_Date} = "Totals";
+% $values {Statistics_Created_Count} = $Totals{Statistics_Created_Count};
+% $values {Statistics_Resolved_Count} = $Totals{Statistics_Resolved_Count};
+% $values {Statistics_Deleted_Count} = $Totals{Statistics_Deleted_Count};
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@BoldFormat, i => $line, record => $record, maxitems => $maxitems &>
+</table>
+</&>
+
+<hr>
+
+<BR />
+<BR />
+
+<%perl>
+# Create the graph URL
+my $url= 'Elements/Chart?x_labels=';
+for (1..$diff) {
+ $url .= $data[0][$_] . ",";
+}
+chop $url;
+$url .= "&";
+shift @data;
+for (0..$#data) {
+ $url .= "data".(1+$_)."=".(join ",", @{$data[$_]})."&";
+}
+chop $url;
+$url .= "&set_legend=Created,Resolved,Deleted";
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox,
+ Title => "Change Queue or Dates",
+ ShowDates => 1, sMonth => \$sMonth, sDay => \$sDay, sYear => \$sYear,
+ eMonth => \$eMonth, eDay => \$eDay, eYear => \$eYear,
+ weekends => $weekends,
+ ShowSingleQueue => 1, Queue => $Queue
+ &>
+
+</form>
+
+<a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/index.html?<% $QueryString %>"><&|/l&>Bookmarkable link</&></a> |
+<a href="<%$RT::WebPath%>/RTx/Statistics/CallsQueueDay/Results.tsv?<%$QueryString%>"><&|/l&>spreadsheet</&></a>
+<BR>
+<BR>
+
+
+% Statistics::DebugLog("ref of eMonth is " . ref($eMonth) . "\n");
+% Statistics::DebugInit( $m );
+
+<%ARGS>
+$Queue => undef
+$weekends => $Statistics::PerDayWeekends;
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$currentMonth=>undef
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+my $selected;
+my $n = 0;
+my @data = ([]);
+my @dates;
+my @msgs;
+my $diff;
+my $sEpoch=0;
+my $eEpoch=0;
+my %Totals;
+my $QueryString;
+my $maxitems = 4;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+
+# If debugging, set things up and display all the args
+Statistics::DebugClear();
+Statistics::DebugLog("CallsQueueDay/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+ Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+my $Format = qq{ Statistics_Date,
+ '__Statistics_Created_Count__/STYLE:text-align:right;',
+ '__Statistics_Resolved_Count__/STYLE:text-align:right;',
+ '__Statistics_Deleted_Count__/STYLE:text-align:right;' };
+my $BoldFormat = qq{ '<B>__Statistics_Date__</B>',
+ '<B>__Statistics_Created_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Resolved_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Deleted_Count__</B>/STYLE:text-align:right;' };
+my (@Format) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $Format);
+my (@BoldFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldFormat);
+Statistics::DebugLog("CallsQueueDay/index.html Format array=" . join(',', @Format) . "\n");
+
+if (!defined $Queue) {
+ my $QueueObj = new RT::Queue($session{'CurrentUser'});
+ $QueueObj->Load($Statistics::PerDayQueue);
+ $Queue = $QueueObj->Id();
+}
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+ $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+ $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+ $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+ # This case happens when the page is first loaded
+ my @local = localtime(time);
+ ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+ $eYear += 1900;
+ $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+ # We have an end, but not a start, or, overlapping.
+
+ # if $currentMonth is set, just set the day to 1
+ if($currentMonth) {
+ # set start vars from end, but with day set to 1
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+ $sDay=1;
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+ } else {
+ # If the user has specified how many days back to go, use that,
+ # If not, set start to configured default period before end
+ if(defined $days) {
+ $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+ } else {
+ $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+ }
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+ }
+ $sYear += 1900;
+}
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+$diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+Statistics::DebugLog("Setting diff=$diff\n");
+
+Statistics::DebugLog("sEpoch=$sEpoch, components=" . join(',', localtime($sEpoch)) . "\n");
+Statistics::DebugLog("eEpoch=$eEpoch, components=" . join(',', localtime($eEpoch)) . "\n");
+
+# Set up the string for the current query for bookmarkable link
+$QueryString = "sDay=$sDay&sMonth=$sMonth&sYear=$sYear&eDay=$eDay&eMonth=$eMonth&eYear=$eYear&weekends=$weekends&Queue=$Queue";
+
+# Set up the end date to be midnight(morning) of the date after the one the user wanted.
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($Queue);
+$n = 0;
+until ($#dates == $diff) {
+ my $date = new RT::Date($session{CurrentUser});
+ $date->Set(Value=>$endRange - $n, Format => 'unix');
+ # Note: we used to adjust the time to local midnight, but
+ # none of the other date entry fields in RT seem to adjust, so we've stopped.
+ #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+ $n+= $Statistics::secsPerDay;
+ # If we aren't showing weekends and this is one, decrement the number
+ # of days to show and skip to the next date.
+ if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+ unshift @dates, $date;
+Statistics::DebugLog("pushing date " . Statistics::FormatDate("%c", $date) . "\n");
+ unshift @{ $data[0] }, Statistics::FormatDate($Statistics::PerDayLabelDateFormat, $date);
+}
+
+# We put an extra day into the lists to cover up till midnight of the next day,
+# But we don't want that to appear in the labels, so pop it off.
+pop( @{ $data[0] } );
+
+</%INIT>
diff --git a/rt/html/RTx/Statistics/DayOfWeek/Elements/Chart b/rt/html/RTx/Statistics/DayOfWeek/Elements/Chart
new file mode 100755
index 000000000..239c09541
--- /dev/null
+++ b/rt/html/RTx/Statistics/DayOfWeek/Elements/Chart
@@ -0,0 +1,26 @@
+% $r->content_type("image/$format");
+% $m->print($graph->plot(\@data)->$format());
+% $m->abort();
+<&|/l, $#data+1&>[_1] Elements</&>:<p>
+% for (0..$#data) {
+<% $data[$_] %><p>
+% }
+<%INIT>
+use GD::Graph::bars;
+
+my @data;
+my $graph = GD::Graph::bars->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+ x_label => 'Day of Week',
+ y_label => 'Ticket actions per Day by type');
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+
+my $format = $graph->export_format;
+$r->content_type("image/$format");
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/DayOfWeek/index.html b/rt/html/RTx/Statistics/DayOfWeek/index.html
new file mode 100755
index 000000000..2e82b9c24
--- /dev/null
+++ b/rt/html/RTx/Statistics/DayOfWeek/index.html
@@ -0,0 +1,155 @@
+<& /Elements/Header, Title =>loc('Tickets by Day Of Week in Queue:' . $QueueObj->Name()) &>
+<& /RTx/Statistics/Elements/Tabs, Title =>loc('Trends in ticket status by Day Of Week in Queue:' . $QueueObj->Name()) &>
+
+<h3>Description</h3>
+<p>The purpose of this page is to show historical trends for each day of the week.
+It displays details of number of tickets created in your
+selected queue for each day. It also hows how many of those created tickets were Resolved or Deleted</p>
+
+<form method="POST" action="index.html">
+
+
+%my $title = "Ticket counts by day of week in " . $QueueObj->Name();
+<&|/Elements/TitleBox,
+ title => $title,
+ title_href => "/RTx/Statistics/DayOfWeek/index.html?$QueryString" &>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH=100%>
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header,
+ Format => \@Format,
+ FormatString => $Format,
+ AllowSorting => $AllowSorting,
+ Order => $Order,
+ Query => undef,
+ Rows => $Rows,
+ Page => $Page,
+ OrderBy => $OrderBy ,
+ BaseURL => $BaseURL,
+ maxitems => $maxitems &>
+% }
+% my $line = 1;
+% for my $d (0..$#days) {
+% my $x = 1;
+% $values{Statistics_Date} = $days[$d];
+%# NOTE Show all status values???
+% $values{Statistics_Created_Count} = $counts[$d]{new};
+% $values{Statistics_Resolved_Count} = $counts[$d]{resolved};
+% $values{Statistics_Deleted_Count} = $counts[$d]{deleted};
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@Format, i => $line, record => $record, maxitems => $maxitems &>
+% $line++;
+% }
+% $values {Statistics_Date} = "Totals";
+% $values {Statistics_Created_Count} = $Totals{new};
+% $values {Statistics_Resolved_Count} = $Totals{resolved};
+% $values {Statistics_Deleted_Count} = $Totals{deleted};
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@BoldFormat, i => $line, record => $record, maxitems => $maxitems &>
+</table>
+</&>
+
+<hr>
+
+<BR />
+<BR />
+
+<%perl>
+my $url = 'Elements/Chart?&x_labels=';
+for (0..$#days) {
+ $url .= $days[$_] . "," ;
+}
+chop $url;
+$url .= "&";
+
+my @things = qw(new resolved deleted);
+for my $th (0..$#things) {
+ $url .= "data".(1+$th)."=".(join ",", map { $counts[$_]{$things[$th]} } (0..6))."&";
+}
+chop $url;
+$url .= '&set_legend=Created,Resolved,Deleted';
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+% Statistics::DebugLog("queue name=" . $QueueObj->Id() . "\n");
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox,
+ Title => "Change Queue",
+ ShowSingleQueue => 1, Queue => $QueueObj->Id()
+ &>
+
+</form>
+
+% Statistics::DebugInit( $m );
+
+<%ARGS>
+$Queue => $Statistics::DayOfWeekQueue
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+</%ARGS>
+
+<%INIT>
+use GD::Graph;
+use RTx::Statistics;
+my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
+my $n = 0;
+my @data = ([]);
+my @msgs;
+my @counts;
+my %Totals = (
+ resolved => 0,
+ deleted => 0,
+ new => 0
+);
+my $QueryString = "Queue=$Queue";
+my $maxitems = 4;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+my $Format = qq{ Statistics_Date,
+ '__Statistics_Created_Count__/STYLE:text-align:right;',
+ '__Statistics_Resolved_Count__/STYLE:text-align:right;',
+ '__Statistics_Deleted_Count__/STYLE:text-align:right;' };
+my $BoldFormat = qq{ '<B>__Statistics_Date__</B>',
+ '<B>__Statistics_Created_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Resolved_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Deleted_Count__</B>/STYLE:text-align:right;' };
+my (@Format) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $Format);
+my (@BoldFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldFormat);
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($Queue);
+$RT::Logger->warning("Loaded queue $Queue, name=". $QueueObj->Name());
+
+my $tix = new RT::Tickets($session{'CurrentUser'});
+$tix->LimitQueue (VALUE => $Queue);
+$tix->UnLimit;
+if ($tix->Count) {
+ # Initialize the counters to zero, so that all the cells show up
+ foreach my $day (0..@days) {
+ $counts[$day]{resolved} = 0;
+ $counts[$day]{deleted} = 0;
+ $counts[$day]{new} = 0;
+ }
+ while (my $t = $tix->RT::SearchBuilder::Next) { # BLOODY HACK
+ if($t->Status eq "resolved") {
+ $counts[(localtime($t->ResolvedObj->Unix))[6]]{resolved}++;
+ $Totals{resolved}++;
+ }
+ if($t->Status eq "deleted") {
+ $counts[(localtime($t->LastUpdatedObj->Unix))[6]]{deleted}++;
+ $Totals{deleted}++;
+ }
+ $counts[(localtime($t->CreatedObj->Unix))[6]]{new}++;
+ $Totals{new}++;
+ }
+}
+</%INIT>
diff --git a/rt/html/RTx/Statistics/DurationAsString b/rt/html/RTx/Statistics/DurationAsString
new file mode 100755
index 000000000..c0b4d9af4
--- /dev/null
+++ b/rt/html/RTx/Statistics/DurationAsString
@@ -0,0 +1,18 @@
+<%$days|'00'%> days <%$hours|'00'%>:<%$minutes|'00'%>
+<%INIT>
+
+my $MINUTE = 60;
+my $HOUR = $MINUTE*60;
+my $DAY = $HOUR * 24;
+my $WEEK = $DAY * 7;
+my $days = int($Duration / $DAY);
+$Duration = $Duration % $DAY;
+my $hours = int($Duration / $HOUR);
+$hours = sprintf("%02d", $hours);
+$Duration = $Duration % $HOUR;
+my $minutes = int($Duration/$MINUTE);
+$minutes = sprintf("%02d", $minutes);
+</%INIT>
+<%ARGS>
+$Duration => undef
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/Elements/CollectionAsTable/Header b/rt/html/RTx/Statistics/Elements/CollectionAsTable/Header
new file mode 100644
index 000000000..cecb02eee
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/CollectionAsTable/Header
@@ -0,0 +1,126 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+@Format => undef
+$FormatString => undef
+$AllowSorting => undef
+$Order=>undef
+$BaseURL => undef
+$Query => undef
+$Rows => undef
+$Page => undef
+$maxitems => undef
+</%ARGS>
+<TR class="collection-as-table">
+<%perl>
+
+my %generic_query_args = ( Query => $Query, Rows => $Rows, Page => $Page, Format => $FormatString );
+
+my $item = 0;
+foreach my $col (@Format) {
+ $item++;
+ if ( $col->{title} eq 'NEWLINE' ) {
+ while ( $item < $maxitems ) {
+ $m->out(qq{<th class="collection-as-table">&nbsp;</th>\n});
+ $item++;
+ }
+
+ $item = 0;
+ $m->out(qq{</TR>\n<TR class="collection-as-table">});
+ }
+ else {
+ $m->out('<TH class="collection-as-table" ');
+ $m->out( 'align="' . $col->{align} . '"' ) if ( $col->{align} );
+ $m->out( 'style="' . $col->{style} . '"' ) if ( $col->{style} );
+ $m->out('>');
+ my $title = $col->{title};
+ $title =~ s/^__(.*)__$/$1/o;
+ $title = (
+ $m->comp(
+ '/RTx/Statistics/Elements/StatColumnMap',
+ Name => $title,
+ Attr => 'title'
+ )
+ || $title
+ );
+ if (
+ $AllowSorting
+ && $col->{'attribute'}
+ && $m->comp(
+ '/RTx/Statistics/Elements/StatColumnMap',
+ Name => $col->{'attribute'},
+ Attr => 'attribute'
+ )
+ )
+ {
+
+ $m->out(
+ '<a href="' . $BaseURL
+ . $m->comp(
+ '/Elements/QueryString',
+ %generic_query_args,
+ OrderBy => (
+ $m->comp(
+ '/RTx/Statistics/Elements/StatColumnMap',
+ Name => $col->{'attribute'},
+ Attr => 'attribute'
+ )
+ || $col->{'attribute'}
+ ),
+ Order => ( $ARGS{'Order'} eq 'ASC' ? 'DESC' : 'ASC' )
+ )
+ . '">'
+ . loc($title) . '</a>'
+ );
+ }
+ else {
+ $m->out( loc($title) );
+ }
+ $m->out('</TH>');
+ }
+}
+</%perl>
+</TR>
diff --git a/rt/html/Ticket/Elements/ShowReferences b/rt/html/RTx/Statistics/Elements/CollectionAsTable/ParseFormat
index bb323f66c..a482f817e 100644
--- a/rt/html/Ticket/Elements/ShowReferences
+++ b/rt/html/RTx/Statistics/Elements/CollectionAsTable/ParseFormat
@@ -43,30 +43,67 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<UL>
-% while (my $Link = $Ticket->RefersTo->Next) {
-<LI>
-% if ($Link->TargetURI->IsLocal) {
-% my $member = $Link->TargetObj;
+<%ARGS>
+$Format
+</%ARGS>
-<a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$member->Id%>"><%$member->Id%></a>: (<%$member->OwnerObj->Name%>) <%$member->Subject%> [<%$member->Status%>]<br>
-% } else {
-<A HREF="<%$Link->TargetURI->HREF%>"><%$Link->Target%></A>
-% }
-%}
+<%init>
+use Regexp::Common;
+my @Columns;
+while ($Format =~ /($RE{delimited}{-delim=>qq{\'"}}|[{}\w.]+)/go) {
+ my $col = $1;
+ if ($col =~ /^$RE{quoted}$/o) {
+ substr($col,0,1) = "";
+ substr($col,-1,1) = "";
+ }
-% while (my $Link = $Ticket->ReferredToBy->Next) {
-<LI>
-% if ($Link->BaseURI->IsLocal) {
-% my $member = $Link->BaseObj;
-<a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$member->Id%>"><%$member->Id%></a>: (<%$member->OwnerObj->Name%>) <%$member->Subject%> [<%$member->Status%>]<br>
-% } else {
-<A HREF="<%$Link->BaseURI->HREF%>"><%$Link->Base%></A>
-%}
-% }
-</UL>
-<%ARGS>
-$Ticket => undef
-</%ARGS>
+ my $colref;
+
+ # kfh at mqsoftware.com added this to be able
+ # to create columns where the actual heading and value
+ # aren't know ahead of time. For instance queue names.
+ # it will work with subcols, but all subcols will have the same KEY
+ if ( $col =~ s!/KEY:([^/]+)!!io ) {
+ $colref->{'keyname'} = $1;
+ }
+ if ( $col =~ s!/STYLE:([^/]+)!!io ) {
+ $colref->{'style'} = $1;
+ }
+ if ( $col =~ s!/CLASS:([^/]+)!!io ) {
+ $colref->{'class'} = $1;
+ }
+ if ( $col =~ s!/TITLE:([^/]+)!!io ) {
+ $colref->{'title'} = $1;
+ }
+ if ( $col =~ s!/ALIGN:([^\/]+)!!io ) {
+ $colref->{'align'} = $1;
+ }
+ if ( $col =~ /__(.*?)__/gio ) {
+ my @subcols;
+ while ( $col =~ s/^(.*?)__(.*?)__//o ) {
+ push ( @subcols, $1 ) if ($1);
+ push ( @subcols, "__$2__" );
+ $colref->{'attribute'} = $2;
+ }
+ push ( @subcols, $col );
+ @{ $colref->{'output'} } = @subcols;
+ }
+ else {
+ @{ $colref->{'output'} } = ( "__" . $col . "__" );
+ $colref->{'attribute'} = $col;
+ }
+
+ if ( !$colref->{'title'} && grep { /^__(.*?)__$/io }
+ @{ $colref->{'output'} } )
+ {
+ $colref->{'title'} = $1;
+ $colref->{'attribute'} = $1;
+ }
+
+
+ push @Columns, $colref;
+}
+ return(@Columns);
+</%init>
diff --git a/rt/html/RTx/Statistics/Elements/CollectionAsTable/Row b/rt/html/RTx/Statistics/Elements/CollectionAsTable/Row
new file mode 100644
index 000000000..bcfabe5c3
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/CollectionAsTable/Row
@@ -0,0 +1,112 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$i => undef
+@Format => undef
+$record => undef
+$maxitems => undef
+$Depth => undef
+$Warning => undef
+</%ARGS>
+
+<%PERL>
+$m->out('<TR class="' . ( $Warning ? 'warnline' : $i % 2 ? 'oddline' : 'evenline' ) . '" >' );
+my $item;
+foreach my $column (@Format) {
+ if ( $column->{title} eq 'NEWLINE' ) {
+ while ( $item < $maxitems ) {
+ $m->out(qq{<td class="collection-as-table">&nbsp;</td>\n});
+ $item++;
+ }
+ $item = 0;
+ $m->out('</TR>');
+ $m->out('<TR class="'
+ . ( $Warning ? 'warnline' : $i % 2 ? 'oddline' : 'evenline' )
+ . '" >' );
+ next;
+ }
+ $item++;
+ $m->out('<td class="collection-as-table" ');
+ $m->out( 'align="' . $column->{align} . '"' ) if ( $column->{align} );
+ $m->out( 'style="' . $column->{style} . '"' ) if ( $column->{style} );
+ $m->out('>');
+ foreach my $subcol ( @{ $column->{output} } ) {
+ if ( $subcol =~ /^__(.*?)__$/o ) {
+ my $col = $1;
+ my $value = $m->comp(
+ '/RTx/Statistics/Elements/StatColumnMap',
+ Name => $col,
+ Attr => 'value'
+ );
+ my @out;
+
+ if ( $value && ref($value) ) {
+
+ # All HTML snippets are returned by the callback function
+ # as scalar references. Data fetched from the objects are
+ # plain scalars, and needs to be escaped properly.
+ @out =
+ map {
+ ref($_) ? $$_ : $m->interp->apply_escapes( $_ => 'h' )
+ } &{$value}( $record, $i, $column->{keyname} );
+ ;
+ }
+ else {
+
+ # Simple value; just escape it.
+ @out = $m->interp->apply_escapes( $value => 'h' );
+ }
+ s/\n/<br>/gs for @out;
+ $m->out( @out );
+ }
+ else {
+ $m->out($subcol);
+ }
+ }
+ $m->out('</td>');
+}
+$m->out('</TR>');
+</%PERL>
diff --git a/rt/html/RTx/Statistics/Elements/ControlsAsTable/ControlBox b/rt/html/RTx/Statistics/Elements/ControlsAsTable/ControlBox
new file mode 100644
index 000000000..ce043e294
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/ControlsAsTable/ControlBox
@@ -0,0 +1,103 @@
+<table class="box" bgcolor="#336699" style="border-style:none solid solid solid;border-width:1px;border-color:#2E2E8C;" cellpadding="0" cellspacing="0">
+ <tbody>
+ <tr>
+ <th style="color: rgb(51, 102, 153);" class="titlebox">
+ <span class="titleboxclose">
+ <a href="#" onclick="hideshow('stats_control')">X</a></span>&nbsp;
+ <span class="titleboxtitle" style="color: rgb(255, 255, 255);">
+ <b><% $Title %></b></span>
+ </th>
+ <th style="color: rgb(51, 102, 153);" class="titleboxright">
+ <span class="titleboxright">&nbsp;</span>
+ </th>
+ </tr>
+ <tr id="element-stats_control">
+ <td colspan="3" class="" bgcolor="#dddddd">
+ <table border="0" cellpadding="1" cellspacing="0">
+% if (defined $ShowStatus) {
+ <tr>
+ <td class="collection-as-table" style="text-align:left;">Show Status:</td>
+ <td COLSPAN=3 class="collection-as-table" style="text-align:left;">
+ <& /Elements/SelectStatus, Name=>"status", Default => $Status, DefaultValue => undef &>
+ </td>
+ </tr>
+% }
+% if (defined $ShowSingleQueue) {
+ <tr>
+ <td class="collection-as-table" style="text-align:left;">Show Queue:</td>
+ <td COLSPAN=3 class="collection-as-table" style="text-align:left;">
+ <& /Elements/SelectQueue, Name=>"Queue", Default=>$Queue ,ShowNullOption=>0,
+ CheckQueueRight=>'SeeQueue' &>
+ </td>
+ </tr>
+% }
+% if (defined $ShowDates) {
+ <tr>
+ <& /RTx/Statistics/Elements/DateSelectRow, Label => "Start Date:",
+ refMonth => $sMonth, nameMonth => "sMonth",
+ refDay => $sDay, nameDay => "sDay",
+ refYear => $sYear, nameYear => "sYear" &>
+ </tr>
+ <tr>
+ <& /RTx/Statistics/Elements/DateSelectRow, Label => "End Date:",
+ refMonth => $eMonth, nameMonth => "eMonth",
+ refDay => $eDay, nameDay => "eDay",
+ refYear => $eYear, nameYear => "eYear" &>
+ </tr>
+ <tr>
+ <td class="collection-as-table" style="text-align:left;">Show Weekends:</td>
+ <td class="collection-as-table" style="text-align:left;">
+ <select name=weekends>
+ <option value=0 <% (!$weekends) && 'selected' %> >No</option>
+ <option value=1 <% $weekends && 'selected' %> >Yes</option>
+ </select>
+ </td>
+ </tr>
+% }
+% if (defined $ShowMultiQueues) {
+ <tr>
+% if (defined $ShowDates) {
+%# If we're showing the dates, we put these side by side.
+ <td COLSPAN=2 class="collection-as-table" style="text-align:left;">Select All Queues: <input type=checkbox name="AddAllCheck"></td>
+ <td COLSPAN=3 class="collection-as-table" >
+ <& /RTx/Statistics/Elements/SelectMultiQueue, Name=>"queues", Selected=>$queues_ref,
+ ShowNullOption=>0, CheckQueueRight=>'SeeQueue', Size => 10, NamedValues => 1 &>
+ </td>
+% } else {
+ <td COLSPAN=3 class="collection-as-table" style="text-align:left;">
+ <& /RTx/Statistics/Elements/SelectMultiQueue, Name=>"queues", Selected=>$queues_ref,
+ ShowNullOption=>0, CheckQueueRight=>'SeeQueue', Size => 10, NamedValues => 1 &>
+ </td>
+ </tr>
+ <tr>
+ <td class="collection-as-table" style="text-align:left;">Select All Queues: <input type=checkbox name="AddAllCheck"></td>
+% }
+ </tr>
+% }
+ <& /RTx/Statistics/Elements/ControlsAsTable/UpdatePage &>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+<BR>
+<%args>
+$Title => undef
+$ShowMultiQueues => undef
+$queues_ref => undef
+$ShowDates => undef
+$sMonth => undef
+$sDay => undef
+$sYear => undef
+$eMonth => undef
+$eDay => undef
+$eYear => undef
+$weekends => undef
+$ShowSingleQueue => undef
+$Queue => undef
+$ShowStatus => undef
+$Status => undef
+</%args>
+
diff --git a/rt/html/RTx/Statistics/Elements/ControlsAsTable/UpdatePage b/rt/html/RTx/Statistics/Elements/ControlsAsTable/UpdatePage
new file mode 100644
index 000000000..b4ccfd56f
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/ControlsAsTable/UpdatePage
@@ -0,0 +1,5 @@
+<tr>
+ <td colspan="4" style="text-align:center;padding-top:3px; background-color:#C8C8C8;">
+ <INPUT TYPE="submit" VALUE="<&|/l&>Update Page</&>">
+ </td>
+</tr>
diff --git a/rt/html/RTx/Statistics/Elements/DateSelectRow b/rt/html/RTx/Statistics/Elements/DateSelectRow
new file mode 100644
index 000000000..325e168c9
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/DateSelectRow
@@ -0,0 +1,55 @@
+ <td class="collection-as-table" style="text-align:left;"><% $Label %></td>
+ <td class="collection-as-table" style="text-align:left;">
+ <select name=<% $nameMonth %> >
+% for ($n=0;$n<=$#Statistics::months;$n++){
+% if ($$refMonth eq $n){
+% $selected ="selected";
+% }else {
+% $selected ="";
+% }
+ <option value=<% $n %> <% $selected %> ><% $Statistics::months[$n] %></option>
+%}
+ </select>
+ </td>
+ <td class="collection-as-table" style="text-align:left;">
+ <select name=<% $nameDay %> >
+% for ($n=1;$n<=31;$n++){
+% if ($$refDay == $n ){
+% $selected ="selected";
+% }else {
+% $selected ="";
+% }
+ <option value=<% $n %> <% $selected %> ><% $n %></option>
+% }
+ </select>
+ </td>
+ <td class="collection-as-table" style="text-align:left;">
+ <select name=<% $nameYear %> >
+%
+% for ($n=0;$n <= scalar @Statistics::years-1;$n++){
+% if ($Statistics::years[$n] == $$refYear){
+% $selected ="selected";
+% }else{
+% $selected ="";
+% }
+ <option value=<% $Statistics::years[$n] %> <% $selected %> ><% $Statistics::years[$n] %></option>
+% }
+ </select>
+ </td>
+
+
+<%args>
+$Label => undef
+$refMonth => undef
+$nameMonth => undef
+$refDay => undef
+$nameDay => undef
+$refYear => undef
+$nameYear => undef
+</%args>
+<%init>
+use RTx::Statistics;
+my $n;
+my $selected;
+
+</%init>
diff --git a/rt/html/RTx/Statistics/Elements/DurationAsString b/rt/html/RTx/Statistics/Elements/DurationAsString
new file mode 100755
index 000000000..c0b4d9af4
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/DurationAsString
@@ -0,0 +1,18 @@
+<%$days|'00'%> days <%$hours|'00'%>:<%$minutes|'00'%>
+<%INIT>
+
+my $MINUTE = 60;
+my $HOUR = $MINUTE*60;
+my $DAY = $HOUR * 24;
+my $WEEK = $DAY * 7;
+my $days = int($Duration / $DAY);
+$Duration = $Duration % $DAY;
+my $hours = int($Duration / $HOUR);
+$hours = sprintf("%02d", $hours);
+$Duration = $Duration % $HOUR;
+my $minutes = int($Duration/$MINUTE);
+$minutes = sprintf("%02d", $minutes);
+</%INIT>
+<%ARGS>
+$Duration => undef
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/Elements/GraphBox b/rt/html/RTx/Statistics/Elements/GraphBox
new file mode 100644
index 000000000..3dc06973e
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/GraphBox
@@ -0,0 +1,27 @@
+<div style="float:left; padding-right:30px;">
+<table class="box" bgcolor="#336699" style="border-style:none solid solid solid;border-width:1px;border-color:#2E2E8C;" cellpadding="0" cellspacing="0">
+ <tbody><tr>
+ <th style="color: rgb(51, 102, 153);" class="titlebox">
+ <span class="titleboxclose">
+ <a href="#" onclick="hideshow('stats_chart')">X</a></span>&nbsp;
+
+ <span class="titleboxtitle">
+ <b><a href="<% $GraphURL %>">Download Chart as Image</a></b>
+ </span>
+ </th>
+ <th style="color: rgb(51, 102, 153);" class="titleboxright">
+ <span class="titleboxright">&nbsp;</span>
+ </th>
+ </tr>
+
+ <tr id="element-stats_chart">
+ <td colspan="3" class="" bgcolor="#dddddd">
+ <img src="<% $GraphURL %>" ALT="Result Graph" >
+ </td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<%args>
+$GraphURL => undef
+</%args>
diff --git a/rt/html/Ticket/Elements/ShowMemberOf b/rt/html/RTx/Statistics/Elements/SelectMultiQueue
index e443132bc..637f6dc80 100644..100755
--- a/rt/html/Ticket/Elements/ShowMemberOf
+++ b/rt/html/RTx/Statistics/Elements/SelectMultiQueue
@@ -43,15 +43,39 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<UL>
-% my $memberof = $Ticket->MemberOf;
-% while (my $member_of = $memberof->Next) {
-<LI><a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$member_of->Id%>"><%$member_of->Id%></a>: <%$member_of->Subject%> [<%$member_of->Status%>]
+<SELECT NAME ="<%$Name%>" multiple size="<% $Size %>">
+% if ($ShowNullOption) {
+<OPTION VALUE="">-</OPTION>
% }
-</UL>
+% while (my $queue=$q->Next) {
+% if ($ShowAllQueues || $queue->CurrentUserHasRight($CheckQueueRight)) {
+% my $targ="," . $queue->Name . ",";
+<OPTION VALUE="<%($NamedValues ? $queue->Name : $queue->Id) %>" <%( ($sel_list =~ m/$targ/) ? 'SELECTED' : '')%>><%$queue->Name%>
+% if (($Verbose) and ($queue->Description) ){
+(<%$queue->Description%>)
+% }
+</OPTION>
+% }
+% }
+</SELECT>
+<%ARGS>
+$CheckQueueRight => 'CreateTicket'
+$ShowNullOption => 1
+$ShowAllQueues => 1
+$Name => undef
+$Verbose => undef
+$NamedValues => 0
+$Selected => undef # ref to array containing selected queue names
+$Lite => 0
+$Size => 5
+</%ARGS>
<%INIT>
+
+# put list of queue names into string, starting and ending with commas
+my $sel_list = "," . join(",", @$Selected) . ",";
+
+my $q=new RT::Queues($session{'CurrentUser'});
+$q->UnLimit;
+
</%INIT>
-<%ARGS>
-$Ticket => undef
-</%ARGS>
diff --git a/rt/html/RTx/Statistics/Elements/StatColumnMap b/rt/html/RTx/Statistics/Elements/StatColumnMap
new file mode 100644
index 000000000..aef9e2f3e
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/StatColumnMap
@@ -0,0 +1,173 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+$Name => undef
+$Attr => undef
+</%ARGS>
+
+
+<%ONCE>
+our ( $STAT_COLUMN_MAP );
+
+sub StatColumnMap {
+ my $name = shift;
+ my $attr = shift;
+
+ # First deal with the simple things from the map
+ if ( $STAT_COLUMN_MAP->{$name} ) {
+ return ( $STAT_COLUMN_MAP->{$name}->{$attr} );
+ }
+
+ # now, let's deal with harder things, like Custom Fields
+
+ elsif ( $name =~ /^(?:CF|CustomField)\.\{(.+)\}$/ ) {
+ my $field = $1;
+
+ if ( $attr eq 'attribute' ) {
+ return (undef);
+ }
+ elsif ( $attr eq 'title' ) {
+ return ( $field );
+ }
+ elsif ( $attr eq 'value' ) {
+ # Display custom field contents, separated by newlines.
+ # For Image custom fields we also show a thumbnail here.
+ return sub {
+ my $values = $_[0]->CustomFieldValues($field);
+ return map {
+ (
+ ($_->CustomFieldObj->Type eq 'Image')
+ ? \($m->scomp( '/Elements/ShowCustomFieldImage', Object => $_ ))
+ : $_->Content
+ ),
+ \'<br>',
+ } @{ $values->ItemsArrayRef }
+ };
+ }
+ }
+}
+
+sub LinkCallback {
+ my $method = shift;
+
+ my $mode = $RT::Ticket::LINKTYPEMAP{$method}{Mode};
+ my $type = $RT::Ticket::LINKTYPEMAP{$method}{Type};
+ my $mode_uri = $mode.'URI';
+ my $local_type = 'Local'.$mode;
+
+ return sub {
+ map {
+ \'<A HREF="',
+ $_->$mode_uri->Resolver->HREF,
+ \'">',
+ ( $_->$mode_uri->IsLocal ? $_->$local_type : $_->$mode ),
+ \'</A><BR>',
+ } @{ $_[0]->Links($mode,$type)->ItemsArrayRef }
+ }
+}
+
+$STAT_COLUMN_MAP = {
+ LastUpdated => {
+ attribute => 'LastUpdated',
+ title => 'Last Updated',
+ value => sub { return $_[0]->LastUpdatedObj->AsString }
+ },
+
+ Statistics_Date => {
+ title => 'Date',
+ value => sub { return $_[0]{values}{Statistics_Date} }
+ },
+
+ Statistics_Created_Count => {
+ title => 'Created',
+ value => sub { return $_[0]{values}{Statistics_Created_Count} }
+ },
+
+ Statistics_Resolved_Count => {
+ title => 'Resolved',
+ value => sub { return $_[0]{values}{Statistics_Resolved_Count} }
+ },
+
+ Statistics_Deleted_Count => {
+ title => 'Deleted',
+ value => sub { return $_[0]{values}{Statistics_Deleted_Count} }
+ },
+
+ Statistics_Totals => {
+ title => 'Totals',
+ value => sub { return $_[0]{values}{Statistics_Totals} }
+ },
+
+ Statistics_Status => {
+ title => 'Status',
+ value => sub { return $_[0]{values}{Statistics_Status} }
+ },
+
+ Statistics_Dynamic => {
+ # Depends on having a KEY as second param
+ value => sub {
+ my $record = shift;
+ my $line = shift;
+ my $key = shift;
+ return $$record{values}{$key}
+ }
+ },
+
+ # Everything from LINKTYPEMAP
+ (map {
+ $_ => { value => LinkCallback( $_ ) }
+ } keys %RT::Ticket::LINKTYPEMAP),
+
+ '_CLASS' => {
+ value => sub { return $_[1] % 2 ? 'oddline' : 'evenline' }
+ },
+
+};
+</%ONCE>
+<%init>
+$m->comp( '/Elements/Callback', STAT_COLUMN_MAP => $STAT_COLUMN_MAP, _CallbackName => 'StatColumnMap');
+return StatColumnMap($Name, $Attr);
+</%init>
diff --git a/rt/html/RTx/Statistics/Elements/Tabs b/rt/html/RTx/Statistics/Elements/Tabs
new file mode 100755
index 000000000..4fde113ea
--- /dev/null
+++ b/rt/html/RTx/Statistics/Elements/Tabs
@@ -0,0 +1,72 @@
+%# BEGIN LICENSE BLOCK
+%#
+%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
+<& /Elements/Tabs,
+ tabs => $tabs,
+ current_toptab => 'RTx/Statistics/index.html',
+ current_tab => $current_tab,
+ Title => $Title &>
+
+<%INIT>
+ my $tabs = { A => { title => loc('Tickets per Day'),
+ path => 'RTx/Statistics/CallsQueueDay/index.html',
+ },
+ B => { title => loc('Tickets by status'),
+ path => 'RTx/Statistics/OpenStalled/index.html',
+ },
+ C => { title => loc('Multiple Queues'),
+ path => 'RTx/Statistics/CallsMultiQueue/index.html',
+ },
+ D => { title => loc('Ticket Trends by Day'),
+ path => 'RTx/Statistics/DayOfWeek/index.html',
+ },
+ E => { 'title' => loc('Time to Resolve'),
+ path => 'RTx/Statistics/Resolution/index.html',
+ },
+ F => { 'title' => loc('Resolve Time Graph'),
+ path => 'RTx/Statistics/TimeToResolve/index.html',
+ },
+ Z => { 'title' => loc('FAQ'),
+ path => 'RTx/Statistics/FAQ/index.html',
+ },
+ };
+
+ # Now let callbacks add their extra tabs
+ $m->comp('/Elements/Callback', tabs => $tabs, %ARGS);
+
+ foreach my $tab (sort keys %{$tabs}) {
+ if ($tabs->{$tab}->{'path'} eq $current_tab) {
+ $tabs->{$tab}->{"subtabs"} = $subtabs;
+ $tabs->{$tab}->{"current_subtab"} = $current_subtab;
+ }
+ }
+
+</%INIT>
+
+
+<%ARGS>
+$subtabs => undef
+$current_tab => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/FAQ/index.html b/rt/html/RTx/Statistics/FAQ/index.html
new file mode 100644
index 000000000..e7839eaad
--- /dev/null
+++ b/rt/html/RTx/Statistics/FAQ/index.html
@@ -0,0 +1,23 @@
+<& /Elements/Header, Title => 'FAQ and known issues' &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc("FAQ and Known Issues") &>
+<hr noshade size="1">
+<p>This page will be used to contain known issues and FAQ`s for the Statistics
+package<br />
+This will also be used to clarify limitations of the package as they stand.</p>
+
+<p><strong>What Version of the Statistics package is this?</strong></p>
+<p>0.1.8</p>
+
+<p><strong>What time zone are the charts set to?</strong></p>
+<p>Because of the new programming method of the date functions, the charts are currently built in GMT(UTC). This may once again be
+customisable in a future release.</p>
+
+<p><strong>What is the default date period and queue?</strong></p>
+<p>The default date period is the previous 10 days, except where the chart is over a fixed 7 day period. The default queue is either
+General, or another queue set in your local configuration.</p>
+
+<p><strong>What are the limitations of the date function?</strong></p>
+<p>It has few, but it will not let you chose less than one day. you cannot select an end date before the start date and it is not
+recommended to select a date in the future or an illegal date, such at 30th February. Code has been put in place to trap these, but it may
+not be fool proof.</p>
+<hr size="1" noshade>
diff --git a/rt/html/RTx/Statistics/OpenStalled/Elements/Chart b/rt/html/RTx/Statistics/OpenStalled/Elements/Chart
new file mode 100755
index 000000000..9505881e8
--- /dev/null
+++ b/rt/html/RTx/Statistics/OpenStalled/Elements/Chart
@@ -0,0 +1,27 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::bars;
+
+my @data;
+my $graph = GD::Graph::bars->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+ x_label => 'Queue name',
+ y_label => 'Total per queue by status');
+my $format = $graph->export_format;
+$graph->set_legend(split /,/ , $ARGS{set_legend});
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/OpenStalled/Results.tsv b/rt/html/RTx/Statistics/OpenStalled/Results.tsv
new file mode 100644
index 000000000..2ec1e0c4a
--- /dev/null
+++ b/rt/html/RTx/Statistics/OpenStalled/Results.tsv
@@ -0,0 +1,114 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2005 Best Practical Solutions, LLC
+%# <jesse@bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<%ARGS>
+@queues => @Statistics::OpenStalledQueueList
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+
+ my $n = 0;
+ my @data;
+ my @msgs;
+ my %totals;
+ my $QueryString;
+ my $now = new RT::Date($session{CurrentUser});
+ my $tix = new RT::Tickets($session{'CurrentUser'});
+
+ my %queues = map {
+ $_ => 1;
+ } (@queues);
+
+ # set content type
+ $r->content_type('application/vnd.ms-excel');
+
+ $QueryString = "queues=" . join("&queues=", @queues);
+
+ my $queue = new RT::Queues($session{CurrentUser});
+ $queue->UnLimit;
+
+ my $QueueObj = new RT::Queue($session{'CurrentUser'});
+ $QueueObj->Load($queue);
+
+ # Put out some data about the generation of this file
+ $m->out("Tickets by Status by Queue for Queues:\t" . join(',', @queues) . "\tGenerated at:\t" . Statistics::FormatDate("%x %X", $now). "\n\n");
+
+ # basically the same as index.html
+
+ # Output header row
+ $m->out("Status");
+ for ( sort keys %queues) {
+ push @data, $_;
+ my $Queueobj = new RT::Queue($session{'CurrentUser'});
+ $Queueobj->Load($_);
+ next if !$Queueobj->CurrentUserHasRight('SeeQueue');
+ $m->out("\t" . $_);
+ }
+ $m->out("\tTotals\n");
+
+ foreach my $s (qw(new open stalled)) {
+ $m->out("$s");
+ my $total=0;
+ foreach my $q (sort keys %queues) {
+ $tix = new RT::Tickets($session{'CurrentUser'});
+ $tix->LimitQueue(VALUE => "$q");
+ $tix->LimitStatus(VALUE => "$s");
+ $totals{$q} += $tix->Count; # Add up columns for each queue
+ $m->out("\t" . $tix->Count);
+ $total += $tix->Count;
+ }
+ $m->out("\t$total\n");
+ $totals{"Totals"} += $total;
+ }
+ $m->out("Totals");
+ foreach my $q (sort keys %queues) {
+ $m->out("\t" . $totals{$q});
+ }
+ $m->out("\t" . $totals{"Totals"} . "\n");
+
+ $m->abort();
+</%INIT>
diff --git a/rt/html/RTx/Statistics/OpenStalled/index.html b/rt/html/RTx/Statistics/OpenStalled/index.html
new file mode 100755
index 000000000..d0cd9f158
--- /dev/null
+++ b/rt/html/RTx/Statistics/OpenStalled/index.html
@@ -0,0 +1,188 @@
+<& /Elements/Header, Title => loc('New, Open and Stalled tickets by Queue') &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc('New, Open and Stalled tickets by Queue') &>
+
+<h3>Description</h3>
+<p>The purpose of this page is to show a snapshot of the current status of tickets by Queue. You can multi select Queues from the dropdown
+list or simply show all available queues. This will indicate how many tickets have not yet been viewed (New), how many have been at least
+viewed once (Open) and how many have had their status changed to stalled.</p>
+
+<form method="POST" action="index.html">
+
+%my $tix = new RT::Tickets($session{'CurrentUser'});
+%if ($queue) {
+% $tix->LimitQueue (VALUE => $queue);
+%}
+
+
+%my $title = "New, Open and Stalled Tickets in " . join(', ', @queues);
+<& /Elements/TitleBoxStart, title => $title, title_href => "/RTx/Statistics/OpenStalled/index.html?$QueryString"&>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH="100%">
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header,
+ Format => \@RowFormat,
+ FormatString => $RowFormat,
+ AllowSorting => $AllowSorting,
+ Order => $Order,
+ Query => undef,
+ Rows => $Rows,
+ Page => $Page,
+ OrderBy => $OrderBy ,
+ BaseURL => $BaseURL,
+ maxitems => $maxitems &>
+% }
+
+% for ( sort keys %queues_to_show) {
+% push @data, $_;
+% }
+% my @legend;
+% my $total = 0;
+% my $line = 0;
+%# NOTE need to handle all status values (see share/html/Elements/SelectStatus).
+% foreach my $s (qw(new open stalled)) {
+% $line++;
+% push @legend, $s;
+% $total=0;
+% foreach my $q (sort keys %queues_to_show) {
+% $tix = new RT::Tickets($session{'CurrentUser'});
+% $tix->LimitQueue(VALUE => "$q");
+% $tix->LimitStatus(VALUE => "$s");
+% push @data, $tix->Count;
+% $totals{$q} += $tix->Count; # Add up columns for each queue
+% $total += $tix->Count;
+% $values{$q} = $tix->Count;
+% }
+% $totals{"Totals"} += $total;
+% $values{Statistics_Status} = $s;
+% $values{Statistics_Totals} = $total;
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@RowFormat, i => $line, record => $record, maxitems => $maxitems &>
+% }
+% $values{Statistics_Status} = "Totals";
+% foreach my $q (sort keys %queues_to_show) {
+% $values{$q} = $totals{$q};
+% }
+% $values{Statistics_Totals} = $totals{"Totals"};
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@BoldRowFormat, i => $line+1, record => $record, maxitems => $maxitems &>
+</table>
+<& /Elements/TitleBoxEnd&>
+
+<hr>
+
+<BR />
+<BR />
+
+% use Data::Dumper;
+% Statistics::DebugLog("Dump of data array is " . Dumper(@data) . "\n");
+% my $url = 'Elements/Chart?x_labels=';
+% for (1..(scalar keys %queues_to_show)) {
+% $url .= $m->interp->apply_escapes((shift @data),'u') . ',';
+% }
+% chop $url;
+% $url .= '&data1=' ;
+% for (1..(scalar keys %queues_to_show)) {
+% $url .= $m->interp->apply_escapes((shift @data),'u') . ',';
+% }
+% chop $url;
+% $url .= '&data2=' ;
+% for (1..(scalar keys %queues_to_show)) {
+% $url .= $m->interp->apply_escapes((shift @data),'u') . ',';
+% }
+% chop $url;
+% $url .= '&data3=' ;
+% for (1..(scalar keys %queues_to_show)) {
+% $url .= $m->interp->apply_escapes((shift @data),'u') . ',';
+% }
+% $url .= '&set_legend='.(join ",", @legend);
+
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox, Title => "Select Queues", ShowMultiQueues => 1, queues_ref => \@queues &>
+
+<a href="<%$RT::WebPath%>/RTx/Statistics/OpenStalled/index.html?<% $QueryString %>"><&|/l&>Bookmarkable link</&></a>
+%# | <a href="<%$RT::WebPath%>/RTx/Statistics/OpenStalled/Results.tsv?<%$QueryString%>"><&|/l&>spreadsheet</&></a>
+<BR>
+<BR>
+
+</FORM>
+
+% Statistics::DebugInit( $m );
+
+<%ARGS>
+@queues => @Statistics::OpenStalledQueueList
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+$AddAllCheck => undef
+</%ARGS>
+
+<%INIT>
+ use RTx::Statistics;
+
+ my $n = 0;
+ my @data;
+ my @msgs;
+ my %totals;
+ my $QueryString;
+ my %queues_to_show;
+ my $maxitems;
+ my $RowFormat;
+ my $BoldRowFormat;
+ my %record;
+ my %values;
+ my $record = \%record;
+
+ $record{values} = \%values;
+
+ Statistics::DebugClear();
+
+ # Handle the Add All Checkbox
+ if($AddAllCheck eq "on") {
+ $AddAllCheck = undef;
+ undef (@queues);
+ my $q=new RT::Queues($session{'CurrentUser'});
+ $q->UnLimit;
+ while (my $queue=$q->Next) {
+ next if !$queue->CurrentUserHasRight('SeeQueue');
+ push @queues, $queue->Name;
+ }
+ }
+
+ # If the user has the right to see the queue, put it into the map
+ for my $q (@queues) {
+ my $Queueobj = new RT::Queue($session{'CurrentUser'});
+ $Queueobj->Load($q);
+ next if !$Queueobj->CurrentUserHasRight('SeeQueue');
+ $queues_to_show{$q} = 1;
+ }
+
+ $maxitems = (scalar @queues) + 2;
+
+ # Build the new query string
+ $QueryString = "queues=" . join("&queues=", @queues);
+
+ # Build the format strings
+ $RowFormat = "'__Statistics_Status__'";
+ $BoldRowFormat = "'<B>__Statistics_Status__</B>'";
+ for my $q (@queues) {
+ $RowFormat .= ",'__Statistics_Dynamic__/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+ $BoldRowFormat .= ",'<B>__Statistics_Dynamic__</B>/KEY:$q/TITLE:$q/STYLE:text-align:right;'";
+ }
+ $RowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+ $BoldRowFormat .= ",'<B>__Statistics_Totals__</B>/STYLE:text-align:right;'";
+ # Parse the formats into structures.
+ my (@RowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $RowFormat);
+ my (@BoldRowFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldRowFormat);
+
+
+ my $queue = new RT::Queues($session{CurrentUser});
+ $queue->UnLimit;
+
+ my $QueueObj = new RT::Queue($session{'CurrentUser'});
+ $QueueObj->Load($queue);
+
+</%INIT>
diff --git a/rt/html/RTx/Statistics/Resolution/Elements/Chart b/rt/html/RTx/Statistics/Resolution/Elements/Chart
new file mode 100755
index 000000000..fa0ac5538
--- /dev/null
+++ b/rt/html/RTx/Statistics/Resolution/Elements/Chart
@@ -0,0 +1,29 @@
+<%perl>
+$r->content_type("image/$format");
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new($Statistics::GraphWidth,$Statistics::GraphHeight);
+$graph->set(export_format => "png",
+ x_label => 'Days',
+ y_label => 'Average time in Days');
+
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+
+my $format = $graph->export_format;
+#$r->content_type("image/$format");
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/Resolution/index.html b/rt/html/RTx/Statistics/Resolution/index.html
new file mode 100644
index 000000000..d9885b093
--- /dev/null
+++ b/rt/html/RTx/Statistics/Resolution/index.html
@@ -0,0 +1,269 @@
+<& /Elements/Header, Title => 'Time to Resolution' &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc("Time To Resolve tickets by Queue for : " .$QueueObj->Name()) &>
+<h3>Description</h3>
+<p>This page shows details of resolution of tickets in the selected queue. It displays tickets created on each day in your selected date
+range. Of those tickets created on that day, how many have been resolved and the total time it has taken for all tickets created on that
+day to be resolved.</p>
+<p>At the bottom of the chart is shows total time taken to resolve all tickets
+in the selected date range and the average time per ticket to
+resolve.</p>
+
+<form method="POST" action="index.html">
+
+%my $title = "Time to resolve in " . $QueueObj->Name() . " per day from " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[0]) . " through " .
+% Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$#dates-1]);
+<&|/Elements/TitleBox,
+ title => $title,
+ title_href => "/RTx/Statistics/Resolution/index.html?$QueryString" &>
+<TABLE BORDER=0 cellspacing=0 cellpadding=1 WIDTH=100%>
+% if ($ShowHeader) {
+<& /RTx/Statistics/Elements/CollectionAsTable/Header,
+ Format => \@Format,
+ FormatString => $Format,
+ AllowSorting => $AllowSorting,
+ Order => $Order,
+ Query => undef,
+ Rows => $Rows,
+ Page => $Page,
+ OrderBy => $OrderBy ,
+ BaseURL => $BaseURL,
+ maxitems => $maxitems &>
+% }
+% my $line = 1;
+% LINE: for my $d (0..$#dates ) {
+% if ($d == $#dates ){
+% next LINE;
+% }
+% my $x = 1;
+% $values{Statistics_Date} = Statistics::FormatDate($Statistics::PerDayDateFormat, $dates[$d]);
+% my $tix = new RT::Tickets($session{'CurrentUser'});
+% $tix->LimitCreated(VALUE => $dates[$d]->ISO, OPERATOR => ">=");
+% if ($dates[$d+1]) {
+% $tix->LimitCreated(VALUE => $dates[$d+1]->ISO, OPERATOR => "<=");
+% }
+% if ($Queue) {
+% $tix->LimitQueue (VALUE => $Queue);
+% }
+% $values{Statistics_Created_Count} = $tix->Count;
+% $tix->LimitStatus(VALUE => "resolved");
+% $values{Statistics_Resolved_Count} = $tix->Count;
+% if ($tix->Count) {
+% my @tix = @{$tix->ItemsArrayRef};
+% my $total;
+% $total += ($_->ResolvedObj->Unix - $_->CreatedObj->Unix) for @tix;
+% $size+= ($#tix +1);
+% $grandtotal += $total;
+% $values{Duration} = Statistics::DurationAsString($total);
+% $data[$x++][$d] = int ($total );
+% } else {
+% $values{Duration} = "N/A";
+% }
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@Format, i => $line, record => $record, maxitems => $maxitems &>
+% $line++;
+%}
+% $size =1 if $size==0;
+% $values{text} = "Average time to resolve = " . Statistics::DurationAsString($grandtotal / $size);
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@OneCellFormat, i => $line, record => $record, maxitems => $maxitems &>
+% $line++;
+% $values{text} = "Total time to resolve = " . Statistics::DurationAsString( $grandtotal );
+<& /RTx/Statistics/Elements/CollectionAsTable/Row, Format => \@OneCellFormat, i => $line, record => $record, maxitems => $maxitems &>
+% $line++;
+</table>
+</&>
+
+<hr>
+
+<BR />
+<BR />
+
+<%perl>
+# Create the graph URL
+
+# change the total time to resolve to a floating point number of days
+foreach my $dat(@{$data[1]} ){
+ $dat = ($dat / $Statistics::secsPerDay);
+ $dat = sprintf("%0.4f", $dat);
+}
+
+my $url = 'Elements/Chart?x_labels=';
+for (0..$diff-1) {
+ $url .= $data[0][$_] . ",";
+}
+chop $url;
+shift @data;
+$url .= "&data1=";
+for(0..$diff-1) {
+ $data[0][$_] = 0 if !$data[0][$_];
+ $url .= $data[0][$_] . ",";
+}
+</%perl>
+
+<& /RTx/Statistics/Elements/GraphBox, GraphURL => $url &>
+
+<& /RTx/Statistics/Elements/ControlsAsTable/ControlBox,
+ Title => "Change Queue or Dates",
+ ShowDates => 1, sMonth => \$sMonth, sDay => \$sDay, sYear => \$sYear,
+ eMonth => \$eMonth, eDay => \$eDay, eYear => \$eYear,
+ weekends => $weekends,
+ ShowSingleQueue => 1, Queue => $Queue
+ &>
+
+</form>
+
+<%ARGS>
+$max => $Statistics::TimeToResolveMaxRows
+$Queue => undef
+$weekends =>$Statistics::TimeToResolveWeekends
+$sMonth=>undef
+$sDay=>undef
+$sYear=>undef
+$eMonth=>undef
+$eDay=>undef
+$eYear=>undef
+$days=>undef
+$currentMonth=>undef
+
+$AllowSorting => undef
+$Order => undef
+$OrderBy => undef
+$ShowNavigation => 1
+$ShowHeader => 1
+$Rows => 50
+$Page => 1
+$BaseURL => undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+use Time::Local;
+my $n = 0;
+my @data = ([]);
+my @dates;
+my @msgs;
+my $size;
+my $selected;
+my $grandtotal = 0;
+my $diff;
+my $sEpoch=0;
+my $eEpoch=0;
+my $QueryString;
+
+my $maxitems = 4;
+my %record;
+my %values;
+my $record = \%record;
+
+$record{values} = \%values;
+
+
+# If debugging, set things up and display all the args
+Statistics::DebugClear();
+Statistics::DebugLog("CallsQueueDay/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+ Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+my $Format = qq{ Statistics_Date,
+ '__Statistics_Created_Count__/STYLE:text-align:right;',
+ '__Statistics_Resolved_Count__/STYLE:text-align:right;',
+ '__Statistics_Dynamic__/KEY:Duration/TITLE:Time To Resolve/STYLE:text-align:right;' };
+my $BoldFormat = qq{ '<B>__Statistics_Date__</B>',
+ '<B>__Statistics_Created_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Resolved_Count__</B>/STYLE:text-align:right;',
+ '<B>__Statistics_Dynamic__</B>/KEY:Duration/TITLE:Time To Resolve/STYLE:text-align:right;' };
+
+# TODO need way to make this cell do colspan
+my $OneCellFormat = qq{ '<B>__Statistics_Dynamic__</B>/KEY:text/STYLE:text-align:left;','','','' };
+
+my (@Format) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $Format);
+my (@BoldFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $BoldFormat);
+my (@OneCellFormat) = $m->comp('/RTx/Statistics/Elements/CollectionAsTable/ParseFormat', Format => $OneCellFormat);
+
+Statistics::DebugLog("CallsQueueDay/index.html Format array=" . join(',', @Format) . "\n");
+
+if ($sDay > $Statistics::monthsMaxDay{$sMonth}) {
+ $sDay = $Statistics::monthsMaxDay{$sMonth};
+}
+
+if ($eDay > $Statistics::monthsMaxDay{$eMonth}) {
+ $eDay = $Statistics::monthsMaxDay{$eMonth};
+}
+
+if ($sYear){
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear-1900);
+}
+if ($eYear){
+Statistics::DebugLog("eMonth = " . $eMonth . "\n");
+ $eEpoch = timelocal(0, 0, 0, $eDay, $eMonth, $eYear-1900);
+} else {
+ # This case happens when the page is first loaded
+ my @local = localtime(time);
+ ($eDay, $eMonth, $eYear) = ($local[3], $local[4], $local[5]);
+ $eYear += 1900;
+ $eEpoch = timelocal(0, 0, 0, $local[3], $local[4], $local[5], $local[6], $local[7], $local[8]);
+Statistics::DebugLog("Setting eEpoch=$eEpoch from current time.\n");
+}
+
+if (($eEpoch < $sEpoch) || ($sEpoch == 0)) {
+ # We have an end, but not a start, or, overlapping.
+
+ # if $currentMonth is set, just set the day to 1
+ if($currentMonth) {
+ # set start vars from end, but with day set to 1
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($eEpoch);
+ $sDay=1;
+ $sEpoch = timelocal(0, 0, 0, $sDay, $sMonth, $sYear);
+ } else {
+ # If the user has specified how many days back to go, use that,
+ # If not, set start to configured default period before end
+ if(defined $days) {
+ $sEpoch = $eEpoch - ($days * $Statistics::secsPerDay);
+ } else {
+ $sEpoch = $eEpoch - ($Statistics::PerDayPeriod * $Statistics::secsPerDay);
+ }
+ (undef, undef, undef, $sDay, $sMonth, $sYear) = localtime($sEpoch);
+ }
+ $sYear += 1900;
+}
+
+# Compute days to chart.
+# The +1 is because we need to generate one more date. If the user
+# selected a 10 day range, we need to generate 11 days.
+$diff = int(($eEpoch - $sEpoch + $Statistics::secsPerDay - 1) / $Statistics::secsPerDay)+1;
+Statistics::DebugLog("Setting diff=$diff\n");
+
+Statistics::DebugLog("sEpoch=$sEpoch, components=" . join(',', localtime($sEpoch)) . "\n");
+Statistics::DebugLog("eEpoch=$eEpoch, components=" . join(',', localtime($eEpoch)) . "\n");
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+if (!defined $Queue) {
+ $QueueObj->Load($Statistics::TimeToResolveQueue);
+ $Queue = $QueueObj->Id();
+}
+
+# Set up the string for the current query for bookmarkable link
+$QueryString = "sDay=$sDay&sMonth=$sMonth&sYear=$sYear&eDay=$eDay&eMonth=$eMonth&eYear=$eYear&weekends=$weekends&Queue=$Queue";
+
+# Set up the end date to be midnight(morning) of the date after the one the user wanted.
+my $endRange = $eEpoch + $Statistics::secsPerDay;
+$QueueObj->Load($Queue);
+# NOTE: list loop starts at the end of the date range, unshifting dates onto
+# the arrays, so that they end up in start to finish order.
+$eEpoch += $Statistics::secsPerDay;
+$n = 0;
+until ($#dates == $diff ) {
+ my $date = new RT::Date($session{CurrentUser});
+ $date->Set(Value=>$endRange - $n, Format => 'unix');
+ # Note: we used to adjust the time to local midnight, but
+ # none of the other date entry fields in RT seem to adjust, so we've stopped.
+ #Statistics::DebugLog("Before adjust to midnight date " . Statistics::FormatDate("%c", $date) . "\n");
+ $n+= $Statistics::secsPerDay;
+ # If we aren't showing weekends and this is one, decrement the number
+ # of days to show and skip to the next date.
+ if(!$weekends and Statistics::RTDateIsWeekend($date)) {$diff--; next;}
+ unshift @dates, $date;
+Statistics::DebugLog("pushing date " . Statistics::FormatDate("%c", $date) . "\n");
+ unshift @{ $data[0] }, Statistics::FormatDate($Statistics::PerDayLabelDateFormat, $date);
+}
+</%INIT>
diff --git a/rt/html/RTx/Statistics/TimeToResolve/Elements/Chart b/rt/html/RTx/Statistics/TimeToResolve/Elements/Chart
new file mode 100755
index 000000000..a069a7bfb
--- /dev/null
+++ b/rt/html/RTx/Statistics/TimeToResolve/Elements/Chart
@@ -0,0 +1,23 @@
+<%perl>
+print $graph->plot(\@data)->$format();
+$m->abort();
+</%perl>
+<%INIT>
+use GD::Graph::points;
+
+my @data;
+my $graph = GD::Graph::points->new(400,300);
+$graph->set(export_format => "png",
+ marker_size => $ARGS{marker_size},
+ x_label => 'Average time to resolve (Days)',
+ y_label => 'Number of tickets resolved' );
+#$r->content_type("image/$format");
+my $format = $graph->export_format;
+push @data, [split /,/ , $ARGS{x_labels}];
+for (1..((scalar keys %ARGS)-2)) {
+ push @data, [split /,/ , $ARGS{"data".$_}];
+}
+
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/TimeToResolve/index.html b/rt/html/RTx/Statistics/TimeToResolve/index.html
new file mode 100755
index 000000000..2124b538d
--- /dev/null
+++ b/rt/html/RTx/Statistics/TimeToResolve/index.html
@@ -0,0 +1,75 @@
+<& /Elements/Header, Title => 'Time to Resolve in Queue' &>
+<& /RTx/Statistics/Elements/Tabs, Title => 'Time to Resolve, by ticket in Queue:' . $QueueObj->Name() &>
+
+<h3>Description</h3>
+<p>This page displays the same information as the Time to Resolve chart, but in a scattergraph format and only for the previous 7 calendar
+days. It only displays data for tickets which have been resolved. Each division on the Days axis is one day and the granularity of this chart
+is 30 minutes.</p>
+<form method="POST">
+
+<table>
+ <tr>
+ <td>Show Queue:</td>
+ <td COLSPAN=3><& /Elements/SelectQueue, Name=>"queue", Default=>$queue ,ShowNullOption=>0,
+ CheckQueueRight=>'SeeQueue' &></td>
+ </tr>
+</table>
+<INPUT TYPE="submit" VALUE="Update Page"</INPUT>
+</form>
+
+<BR>
+% my $url = 'Elements/Chart?x_labels=';
+% my $i;
+% $url .= join ",", (map {(int($_/2) == $_/2 && (++$i)%2) ? $_/2 : ""} grep {$counts[$_]} 0..($#counts-1)), "longer";
+% $url .= '&';
+% $url .= "marker_size=1&";
+% $url .= "data1=".(join ",", map { $_ || () } @counts)."&";
+% chop $url;
+<IMG SRC="<% $url %>">
+
+<BR>
+
+%Statistics::DebugInit($m);
+
+<%ARGS>
+$queue => undef
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+
+my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
+my $n = 0;
+my @data = ([]);
+my @msgs;
+my @counts;
+
+Statistics::DebugClear();
+Statistics::DebugLog("TimeToResolve/index.html ARGS:\n");
+for my $key (keys %ARGS) {
+ Statistics::DebugLog("ARG{ $key }=" . $ARGS{$key} . "\n");
+}
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+if (!defined $queue) {
+ $QueueObj->Load($Statistics::TimeToResolveGraphQueue);
+ $queue = $QueueObj->Id();
+} else {
+ $QueueObj->Load($queue);
+}
+
+
+my $tix = new RT::Tickets($session{'CurrentUser'});
+$tix->LimitQueue (VALUE => $queue) if $queue;
+$tix->LimitStatus(VALUE => "resolved");
+$tix->UnLimit;
+if ($tix->Count) {
+ while (my $t = $tix->RT::SearchBuilder::Next) { # BLOODY HACK
+ my $when = $t->ResolvedObj->Unix - $t->CreatedObj->Unix;
+ next unless $when > 0; # Doubly bloody hack
+ my $max = (60*60*24*2) / 1800;
+ my $x = int($when / 1800);
+ $counts[$x > $max ? $max : $x]++;
+ }
+}
+</%INIT>
diff --git a/rt/html/RTx/Statistics/UserTest/Elements/Chart b/rt/html/RTx/Statistics/UserTest/Elements/Chart
new file mode 100755
index 000000000..99eb2a2b1
--- /dev/null
+++ b/rt/html/RTx/Statistics/UserTest/Elements/Chart
@@ -0,0 +1,28 @@
+<%perl>
+print $graph->plot(\@data)->$format();
+$m->abort();
+print $#data+1 . " Elements:<p>";
+for (0..$#data) {
+print $data[$_];
+print "<p>";
+}
+</%perl>
+<%INIT>
+use GD::Graph::lines;
+
+my @data;
+my $graph = GD::Graph::lines->new(640,480);
+$graph->set(export_format => "png",
+ x_label => 'Days',
+ y_label => 'Average time in Days');
+
+push @data, [split /,/ , $ARGS{x_labels}];
+push @data, [split /,/ , $ARGS{data1}];
+push @data, [split /,/ , $ARGS{data2}];
+push @data, [split /,/ , $ARGS{data3}];
+
+my $format = $graph->export_format;
+#$r->content_type("image/$format");
+</%INIT>
+<%ARGS>
+</%ARGS>
diff --git a/rt/html/RTx/Statistics/UserTest/index.html b/rt/html/RTx/Statistics/UserTest/index.html
new file mode 100755
index 000000000..7bc25da70
--- /dev/null
+++ b/rt/html/RTx/Statistics/UserTest/index.html
@@ -0,0 +1,54 @@
+<& /Elements/Header, Title => 'Time to Resolve in Queue' &>
+<& /RTx/Statistics/Elements/Tabs, Title => 'Time to Resolve, by ticket in Queue:' . $QueueObj->Name() &>
+
+
+<form method="POST">
+
+See Queue:<BR>
+<& /Elements/SelectQueue, Name=>"queue", Default => "$queue" &>
+<BR>
+<INPUT TYPE="submit" VALUE="Go!"</INPUT>
+</form>
+
+<BR>
+% my $url = 'Elements/Chart?x_labels=';
+% my $i;
+% $url .= join ",", (map {(int($_/2) == $_/2 && (++$i)%2) ? $_/2 : ""} grep {$counts[$_]} 0..($#counts-1)), "longer";
+% $url .= '&';
+% $url .= "marker_size=1&";
+% $url .= "data1=".(join ",", map { $_ || () } @counts)."&";
+% chop $url;
+<IMG SRC="<% $url %>">
+
+<BR>
+
+<%ARGS>
+$queue => $Statistics::TimeToResolveGraphQueue;
+</%ARGS>
+
+<%INIT>
+use RTx::Statistics;
+
+my @days = qw(Sun Mon Tue Wed Thu Fri Sat);
+my $n = 0;
+my @data = ([]);
+my @msgs;
+my @counts;
+
+my $QueueObj = new RT::Queue($session{'CurrentUser'});
+$QueueObj->Load($queue);
+
+my $tix = new RT::Tickets($session{'CurrentUser'});
+$tix->LimitQueue (VALUE => $queue) if $queue;
+$tix->LimitStatus(VALUE => "resolved");
+$tix->UnLimit;
+if ($tix->Count) {
+ while (my $t = $tix->RT::SearchBuilder::Next) { # BLOODY HACK
+ my $when = $t->ResolvedObj->Unix - $t->CreatedObj->Unix;
+ next unless $when > 0; # Doubly bloody hack
+ my $max = (60*60*24*2) / 1800;
+ my $x = int($when / 1800);
+ $counts[$x > $max ? $max : $x]++;
+ }
+}
+</%INIT>
diff --git a/rt/html/RTx/Statistics/index.html b/rt/html/RTx/Statistics/index.html
new file mode 100755
index 000000000..41490de18
--- /dev/null
+++ b/rt/html/RTx/Statistics/index.html
@@ -0,0 +1,59 @@
+%# BEGIN LICENSE BLOCK
+%#
+%# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
+%#
+%# (Except where explictly superceded by other copyright notices)
+%#
+%# Copyright this file (c) 2003 Harald Wagener <hwagener@hamburg.fcb.com>
+%#
+%# 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
+<& /Elements/Header, Title => loc('RT Statistics') &>
+<& /RTx/Statistics/Elements/Tabs, Title => loc('RT Statistics') &>
+
+<&|/l&><h2>Description</h2>
+<p>These 6 options below enable you to display management data from the RT Database in table and graphical forms, enabling trends, bottlenecks, load problems etc to be identified.
+Each contains a description of how the data is displayed and describes the options available to you.</p></&>
+<ul>
+<li><strong><a href="CallsQueueDay/index.html">
+<&|/l&>Tickets per day per Queue</&></a></strong><br />
+<&|/l&>View the number of tickets created, resolved or deleted in a<br /> specific Queue, over the requested period of days</&>
+</li>
+<li><strong><a href="OpenStalled/index.html">
+<&|/l&>Tickets status by Queue</&></a></strong><br>
+<&|/l&>View numbers of new, open and stalled tickets in a selected Queue</&>
+</li>
+<li><strong><a href="CallsMultiQueue/index.html">
+<&|/l&>Tickets per Day in Multiple Queues</&>
+</a></strong><br>
+<&|/l&>View tickets created, resolved or deleted on in one or more Queues<br /> over a specified time period</&>
+</li>
+<li><strong><a href="DayOfWeek/index.html">
+<&|/l&>Tickets per Day of Week (absolute)</&></a></strong><br>
+<&|/l&>View trends showing when tickets are created, resolved or deleted</&>
+</li>
+<li><strong><a href="Resolution/index.html">
+<&|/l&>Time to Resolve</&></a></strong><br>
+<&|/l&>View how long tickets take to be resolved by Queue</&>
+</li>
+</li>
+<li><strong><a href="TimeToResolve/index.html">
+<&|/l&>Time to Resolve (scatter graph)</&></a></strong><br>
+<&|/l&>View a detailed scatter graph of time to resolve tickets by Queue</&>
+</li>
+</ul>
diff --git a/rt/html/Reports/Activity/ActivityDetail.html b/rt/html/Reports/Activity/ActivityDetail.html
new file mode 100644
index 000000000..ef0d830f7
--- /dev/null
+++ b/rt/html/Reports/Activity/ActivityDetail.html
@@ -0,0 +1,83 @@
+<&|Elements/Wrapper, %ARGS, title => loc("Activity detail"),
+ path => "Reports/Activity/ActivityDetail.html",
+ &>
+
+<& Elements/MiniPlot, data => \%counts &>
+
+<table style="width: 100%">
+<tr class="titlerow">
+<th>Queue</th><th>Activity</th><th>Date</th><th>Time</th><th>Ticket #</th><th>User</th><th>Short description</th>
+</tr>
+% for my $item (@items) {
+<tr>
+<td><% $item->{queue} %></td>
+<td><% $item->{status} %></td>
+<td><% $item->{date} %></td>
+<td><% $item->{time} %></td>
+<td><% $item->{id} %></td>
+<td><% $item->{actor} %></td>
+<td><% $item->{notes} %></td>
+</tr>
+% }
+</table>
+
+</&>
+<%args>
+$query => 'id > 0'
+$start => "2005/01/01"
+$end => "2006/01/01"
+</%args>
+<%init>
+
+
+my $summary_tickets = RT::Tickets->new($session{'CurrentUser'});
+$summary_tickets->FromSQL($query . " AND ( Updated >= '$start' AND Updated <= '$end')");
+my %counts;
+while (my $ticket = $summary_tickets->Next) {
+ my $txns = $ticket->Transactions;
+ $txns->Limit(FIELD => 'Created', OPERATOR => '>=', VALUE => $start);
+ $txns->Limit(FIELD => 'Created', OPERATOR => '<=', VALUE => $end);
+ # I think they really don't just want status changes
+ $txns->Limit(FIELD => 'Type', VALUE => 'Status', ENTRYAGGREGATOR => 'OR');
+ $txns->Limit(FIELD => 'Type', VALUE => 'Create');
+
+ while (my $txn = $txns->Next){
+ my $date = substr($txn->Created, 0, 10);
+ # we don't have data on the status of a new ticket, default to 'new'
+ $counts{$date}{$txn->NewValue || 'new'}++;
+ }
+}
+
+
+my $tickets = RT::Tickets->new($session{'CurrentUser'});
+$tickets->FromSQL($query);
+my @items;
+while (my $ticket = $tickets->Next) {
+ my $txns = $ticket->Transactions;
+ $txns->Limit(FIELD => 'Created', OPERATOR => '>=', VALUE => $start);
+ $txns->Limit(FIELD => 'Created', OPERATOR => '<=', VALUE => $end);
+ # I think they really don't just want status changes
+ $txns->Limit(FIELD => 'Type', VALUE => 'Status', ENTRYAGGREGATOR => 'OR');
+ $txns->Limit(FIELD => 'Type', VALUE => 'Create');
+
+ while (my $txn = $txns->Next) {
+ push @items, { queue => $txn->TicketObj->QueueObj->Name,
+ id => $txn->TicketObj->id,
+ date => (split ' ', $txn->CreatedObj->ISO)[0],
+ time => (split ' ', $txn->CreatedObj->ISO)[1],
+ status => $txn->NewValue || 'new',
+ actor => $txn->CreatorObj->Name,
+ notes => ($txn->Content ne 'This transaction appears to have no content' ? substr($txn->Content, 0, 60) : $txn->BriefDescription)
+ };
+ }
+}
+
+@items = sort {
+ $a->{queue} cmp $b->{'queue'}
+ || $a->{'status'} cmp $b->{'status'}
+ || $a->{'id'} <=> $b->{'id'}
+ || $a->{'actor'} cmp $b->{'actor'}
+ || $a->{'notes'} <=> $b->{'notes'}
+} @items;
+
+</%init>
diff --git a/rt/html/Reports/Activity/ActivitySummary.html b/rt/html/Reports/Activity/ActivitySummary.html
new file mode 100644
index 000000000..7bb756fbc
--- /dev/null
+++ b/rt/html/Reports/Activity/ActivitySummary.html
@@ -0,0 +1,61 @@
+<&|Elements/Wrapper, %ARGS, title => loc("Activity summary"),
+ path => "Reports/Activity/ActivitySummary.html",
+ &>
+
+<& Elements/MiniPlot, data => \%queues &>
+
+<table style="width: 100%">
+<tr class="titlerow">
+<th>Queue</th>
+% for my $status (sort keys %status) {
+<th><% $status %></th>
+% }
+<th>Total</th>
+</tr>
+% for my $queue (sort keys %queues) {
+<th class="label"><% $queue %></th>
+% for my $status (sort keys %status) {
+<td><% $queues{$queue}{$status} || 0 %>
+% }
+<td><% $total{$queue} %></td>
+</tr>
+% }
+<tr class="grandtotal">
+<th class="label" >Grand Total</th>
+% for my $status (sort keys %status) {
+<td><% $status{$status} %></td>
+% }
+<td><% $total %></td>
+</table>
+</&>
+<%args>
+$query => 'id > 0'
+$start => "2005/01/01"
+$end => "2006/01/01"
+</%args>
+<%init>
+
+my $tickets = RT::Tickets->new($session{'CurrentUser'});
+$tickets->FromSQL($query . " AND ( Updated >= '$start' AND Updated <= '$end')");
+
+my %queues;
+my %status;
+my %total;
+my $total;
+while (my $ticket = $tickets->Next) {
+ my $txns = $ticket->Transactions;
+ $txns->Limit(FIELD => 'Created', OPERATOR => '>=', VALUE => $start);
+ $txns->Limit(FIELD => 'Created', OPERATOR => '<=', VALUE => $end);
+ $txns->Limit(FIELD => 'Type', VALUE => 'Status', ENTRYAGGREGATOR => 'OR');
+ $txns->Limit(FIELD => 'Type', VALUE => 'Create');
+
+ while (my $txn = $txns->Next) {
+ $queues{$txn->TicketObj->QueueObj->Name}{$txn->NewValue || 'new'}++;
+ $status{$txn->NewValue || 'new'}++;
+ $total{$txn->TicketObj->QueueObj->Name}++;
+ $total++;
+ }
+}
+
+
+</%init>
diff --git a/rt/html/Reports/Activity/Elements/LimitReport b/rt/html/Reports/Activity/Elements/LimitReport
new file mode 100644
index 000000000..7c4aac73b
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/LimitReport
@@ -0,0 +1,23 @@
+<form action="index.html" method="POST" enctype="multipart/form-data">
+Query:
+<textarea name="query" rows="5" cols="80"><% $query %></textarea><br />
+
+Report type: <select name="type">
+<option value="ActivityDetail" <% $ARGS{path} =~ /ActivityDetail/ ? 'selected' : '' %>>Activity detail</option>
+<option value="ActivitySummary" <% $ARGS{path} =~ /ActivitySummary/ ? 'selected' : '' %>>Activity summary</option>
+<option value="ResolutionComments" <% $ARGS{path} =~ /ResolutionComments/ ? 'selected' : '' %>>Resolution comments</option>
+<option value="ResolutionStatistics" <% $ARGS{path} =~ /ResolutionStatistics/ ? 'selected' : '' %>>Resolution statistics</option>
+</select><br />
+
+Start date: <input type="text" name="start" value="<% $start %>" /><br />
+End date: <input type="text" name="end" value="<% $end %>" /><br />
+<& /Elements/Submit, Label => loc('Report') &>
+</form>
+<%args>
+$type => undef
+$start => undef
+$end => undef
+$query => undef
+</%args>
+<%init>
+</%init>
diff --git a/rt/html/Reports/Activity/Elements/MiniPlot b/rt/html/Reports/Activity/Elements/MiniPlot
new file mode 100644
index 000000000..f92032818
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/MiniPlot
@@ -0,0 +1,57 @@
+<table class="miniplot"><tr>
+% for my $major (@major) {
+<td><div class="graph">
+ <ul>
+% my $i = 0;
+% for my $minor (@minor) {
+% my $percent = int( 100 * ($data->{$major}{$minor} || 0) / $max );
+ <li class="c<% ($i % 6) + 1%>" style="width: <% $barwidth %>%;
+ left: <% $baroffset + $each * $i %>%;
+ height: <% $percent %>%;"><div class="data"><% $minor %>: <% $percent %>%</div></li>
+% $i++;
+% }
+ </ul>
+</div></td>
+% }
+</tr><tr>
+% for my $major (@major) {
+<th class="legend"><% $major %></th>
+% }
+</tr>
+</table>
+
+<table class="miniplot"><tr>
+% my $i = 0;
+% for my $minor (@minor) {
+<th><span class="demoblock c<% ($i++ % 6) + 1 %>"></span> <% $minor %></th>
+% }
+</tr>
+</table>
+
+<%args>
+$data
+$major => undef
+$minor => undef
+</%args>
+<%init>
+
+my $max = 1;
+
+my %minor;
+for my $major (keys %{$data}) {
+ for (keys %{$data->{$major}}) {
+ $minor{$_}++;
+ $max = $data->{$major}{$_} if $data->{$major}{$_} > $max;
+ }
+}
+
+my @major = $major ? @{$major} : sort keys %{$data};
+my @minor = $minor ? @{$minor} : sort keys %minor;
+
+return unless @minor and @major;
+
+my $each = int( (100 / @minor) );
+my $barwidth = int( (100 / @minor) * (3/4) );
+my $baroffset = int( (100 / @minor) * (1/8) );
+
+</%init>
diff --git a/rt/html/Reports/Activity/Elements/PrintFooter b/rt/html/Reports/Activity/Elements/PrintFooter
new file mode 100644
index 000000000..fa9f47582
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/PrintFooter
@@ -0,0 +1,7 @@
+<hr/>
+<div style="text-align: center;">
+<%$RT::ReportFooterMessage || 'Proprietary and Confidential' %>
+</div>
+</body>
+</html>
+%$m->abort();
diff --git a/rt/html/Reports/Activity/Elements/PrintHeader b/rt/html/Reports/Activity/Elements/PrintHeader
new file mode 100644
index 000000000..b7c4b3419
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/PrintHeader
@@ -0,0 +1,32 @@
+<%args>
+$title => undef
+$path => undef
+$query => undef
+</%args>
+<HTML>
+<HEAD>
+<TITLE><%$title%></TITLE>
+<link rel="shortcut icon" href="<%$RT::WebImagesURL%>/favicon.png" type="image/png" />
+<link media="all" rel="stylesheet" href="<%$RT::WebPath%>/NoAuth/webrt.css" type="text/css" />
+<link media="print" rel="stylesheet" href="<%$RT::WebPath%>/NoAuth/printrt.css" type="text/css" />
+%# XXX TODO THIS SHOULD NOT BE A TABLE
+<body>
+<table width="100%">
+<tr>
+<td align="left">
+<div id="username">User: <%$session{'CurrentUser'}->Name%></div>
+<div id="reportdate">
+%my $d= RT::Date->new($session{'CurrentUser'}); $d->SetToNow;
+<%$d->AsString%></div>
+</td>
+<td align="center">
+<h1><%$title%></h1>
+</td>
+<td align="right">
+<img src="<%$RT::LogoURL%>" alt="RT Logo"/>
+</td>
+</tr>
+</table>
+<hr/>
+<&|/l&>Report criteria:</&> <%$query%>
+<hr />
diff --git a/rt/html/Reports/Activity/Elements/ScreenFooter b/rt/html/Reports/Activity/Elements/ScreenFooter
new file mode 100644
index 000000000..235b7b306
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/ScreenFooter
@@ -0,0 +1,13 @@
+<& LimitReport, %ARGS &>
+% if ($show_print_link) {
+<div align="right">
+% my %printable_args = %ARGS;
+% delete $printable_args{$_} for (qw/path title mode/);
+% $printable_args{'mode'} = 'print';
+% my $url = $ARGS{'path'} .'?'. join(';', map { $_."=".$printable_args{$_} } keys %printable_args);
+<a href="<%$RT::WebPath|n%>/<%$url|n%>"><&|/l&>Printable version</&></a>
+</div>
+% }
+<%args>
+$show_print_link => 1
+</%args>
diff --git a/rt/html/Reports/Activity/Elements/ScreenHeader b/rt/html/Reports/Activity/Elements/ScreenHeader
new file mode 100644
index 000000000..080efc0dd
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/ScreenHeader
@@ -0,0 +1,8 @@
+<%args>
+$title => undef
+$path => undef
+</%args>
+<& /Elements/Header, Title => $title &>
+<& Tabs,
+ current_subtab => $path,
+ Title => $title &>
diff --git a/rt/html/Reports/Activity/Elements/Tabs b/rt/html/Reports/Activity/Elements/Tabs
new file mode 100644
index 000000000..a9498209e
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/Tabs
@@ -0,0 +1,52 @@
+<& /Elements/Tabs,
+ tabs => $tabs,
+ subtabs => $subtabs,
+ current_toptab => 'Tools/Offline.html',
+ current_tab => 'Reports/Activity/index.html'.$args,
+ Title => $Title &>
+
+<%INIT>
+my $subtabs = {};
+
+my $top = $m->caller_args(-1);
+my $args = "?" . $m->comp( '/Elements/QueryString',
+ query => $top->{query},
+ start => $top->{start},
+ end => $top->{end});
+if ($m->caller_args(-1)->{'query'}) {
+ $current_subtab .= $args;
+ $subtabs = {
+ a => { title => 'Activity detail',
+ path => 'Reports/Activity/ActivityDetail.html'.$args,
+ },
+ b => { title => 'Activity summary',
+ path => 'Reports/Activity/ActivitySummary.html'.$args,
+ },
+ c => { title => 'Resolution comments',
+ path => 'Reports/Activity/ResolutionComments.html'.$args,
+ },
+ d => { title => 'Resolution statistics',
+ path => 'Reports/Activity/ResolutionStatistics.html'.$args,
+ },
+ };
+}
+
+my $tabs = {
+ a => { title => loc('Offline'),
+ path => 'Tools/Offline.html',
+ },
+ r => { title => loc('Reports'),
+ path => 'Reports/Activity/index.html'.$args,
+ subtabs => $subtabs,
+ current_subtab => $current_subtab,
+ }
+ };
+
+</%INIT>
+
+
+<%ARGS>
+$current_tab => undef
+$current_subtab => undef
+$Title => undef
+</%ARGS>
diff --git a/rt/html/Reports/Activity/Elements/Wrapper b/rt/html/Reports/Activity/Elements/Wrapper
new file mode 100644
index 000000000..6f81f5f50
--- /dev/null
+++ b/rt/html/Reports/Activity/Elements/Wrapper
@@ -0,0 +1,16 @@
+<%args>
+$mode => 'screen'
+</%args>
+
+% if ($mode eq 'print') {
+<& PrintHeader, %ARGS &>
+%} else {
+<& ScreenHeader, %ARGS &>
+% }
+<%$m->content |n%>
+% if ($mode eq 'print') {
+<& PrintFooter, %ARGS &>
+%} else {
+<& ScreenFooter, %ARGS &>
+% }
+
diff --git a/rt/html/Reports/Activity/ResolutionComments.html b/rt/html/Reports/Activity/ResolutionComments.html
new file mode 100644
index 000000000..81ca301cc
--- /dev/null
+++ b/rt/html/Reports/Activity/ResolutionComments.html
@@ -0,0 +1,62 @@
+<&|Elements/Wrapper, %ARGS, title => loc("Resolution Comments"),
+ path => "Reports/Activity/ResolutionComments.html",
+ &>
+
+<table style="width: 100%">
+<tr>
+<th>Queue</th><th>Ticket #</th><th>Created</th><th>Resolved</th><th>Time to resolve</th>
+</tr>
+<tr>
+<th colspan="5">Resolution comments</th>
+</tr>
+% for my $item (@items) {
+<tr class="titlerow">
+<td><% $item->{queue} %></td>
+<td><% $item->{id} %></td>
+<td><% $item->{created} %></td>
+<td><% $item->{resolved} %></td>
+<td><% $item->{duration} %></td>
+</tr>
+<tr>
+<td colspan="5"><% $item->{whiteboard} %></td>
+</tr>
+% }
+</table>
+</&>
+
+<%args>
+$query => 'id > 0'
+$start => "2005/01/01"
+$end => "2006/01/01"
+</%args>
+<%init>
+
+use Time::Duration;
+
+my $summary_tickets = RT::Tickets->new( $session{'CurrentUser'} );
+$summary_tickets->FromSQL(
+ $query . " AND (Status = 'resolved') AND ( Updated >= '$start' AND Updated <= '$end')" );
+
+my @items;
+while ( my $ticket = $summary_tickets->Next ) {
+ push @items, {
+ queue => $ticket->QueueObj->Name,
+ id => $ticket->id,
+ created => $ticket->CreatedObj->AsString,
+ resolved => $ticket->ResolvedObj->AsString,
+ duration => Time::Duration::concise(
+ Time::Duration::duration(
+ $ticket->ResolvedObj->Unix - $ticket->CreatedObj->Unix
+ )
+ ),
+ whiteboard => $ticket->FirstCustomFieldValue('Whiteboard')
+ };
+}
+
+@items = sort { $a->{queue} cmp $b->{queue} || $a->{id} <=> $b->{id} } @items;
+
+
+
+
+
+</%init>
diff --git a/rt/html/Reports/Activity/ResolutionStatistics.html b/rt/html/Reports/Activity/ResolutionStatistics.html
new file mode 100644
index 000000000..4ecde2c82
--- /dev/null
+++ b/rt/html/Reports/Activity/ResolutionStatistics.html
@@ -0,0 +1,95 @@
+<&|Elements/Wrapper, %ARGS, title => loc("Resolution statistics"),
+ path => "Reports/Activity/ResolutionStatistics.html",
+ &>
+
+<& Elements/MiniPlot,
+ data => \%plot,
+ major => ['Date range','Last 30 days','Last 60 days','Last 90 days','Ever'],
+ minor => [(sort keys %queues), "Average"]
+ &>
+
+<table style="width: 100%">
+<tr>
+<td></td><th colspan="4">Number of tickets closed / Average resolution time per ticket</th>
+</tr>
+<tr class="titlerow">
+<th>Queue</th>
+<th>Date range</th>
+<th>Last 30 days</th>
+<th>Last 60 days</th>
+<th>Last 90 days</th>
+<th>Ever</th>
+</tr>
+% for my $queue (sort keys %queues) {
+<tr>
+<th><% $queue %></th>
+% for my $period ('Date range','Last 30 days','Last 60 days','Last 90 days','Ever') {
+<td><% scalar @{$closed{$period}{$queue}} %> / <% $average_resolve_times{$period}{$queue} %></td>
+% }
+</tr>
+% }
+<tr class="grandtotal">
+<th>Ticket average</th>
+% for my $period ('Date range','Last 30 days','Last 60 days','Last 90 days','Ever') {
+<td><% $average_resolve_times{$period}{_all_count} %> / <% $average_resolve_times{$period}{_all} %></td>
+% }
+</tr>
+</table>
+
+</&>
+<%args>
+$query => 'id > 0'
+$start => "2005/01/01"
+$end => "2006/01/01"
+</%args>
+<%init>
+
+my $in_30_days = RT::Date->new($session{'CurrentUser'});
+$in_30_days->Set(Format => 'Unix', Value => ( time - (86400*30)));
+my $in_60_days = RT::Date->new($session{'CurrentUser'});
+$in_60_days->Set(Format => 'Unix', Value => ( time - (86400*60)));
+my $in_90_days = RT::Date->new($session{'CurrentUser'});
+$in_90_days->Set(Format => 'Unix', Value => ( time - (86400*90)));
+
+my %queries;
+$queries{'Date range'} = "(Resolved >= '$start' AND Resolved <= '$end')";
+$queries{'Last 30 days'} = "(Resolved >= '".$in_30_days->ISO."')";
+$queries{'Last 60 days'} = "(Resolved >= '".$in_60_days->ISO."')";
+$queries{'Last 90 days'} = "(Resolved >= '".$in_90_days->ISO."')";
+$queries{'Ever'} = "(Status = 'resolved' OR Status = 'rejected')";
+
+
+my %closed;
+my %queues;
+foreach my $period (keys %queries) {
+ my $tix = RT::Tickets->new($session{'CurrentUser'});
+ $tix->FromSQL($query . " AND " .$queries{$period});
+
+ while (my $ticket = $tix->Next) {
+ push @{ $closed{$period}{$ticket->QueueObj->Name}}, $ticket;
+ $queues{$ticket->QueueObj->Name}++;
+ }
+}
+
+my %restimes;
+my %average_resolve_times;
+my %plot;
+use Time::Duration;
+foreach my $period ( keys %closed ) {
+ foreach my $queue ( keys %{$closed{$period}} ) {
+ foreach my $ticket (@{$closed{$period}{$queue}} ) {
+ push @{$restimes{$period}{$queue}}, ( $ticket->ResolvedObj->Unix - $ticket->CreatedObj->Unix);
+ }
+
+ my $total_time = 0;
+ $total_time+= $_ for @{$restimes{$period}{$queue}};
+ $average_resolve_times{$period}{'_all_time'} += $total_time;
+ $average_resolve_times{$period}{'_all_count'} += @{$restimes{$period}{$queue}};
+ $plot{$period}{$queue} = $total_time / @{$restimes{$period}{$queue}};
+ $average_resolve_times{$period}{$queue} = Time::Duration::concise(Time::Duration::duration($plot{$period}{$queue}));
+ }
+ $plot{$period}{Average} = $average_resolve_times{$period}{'_all_time'} / $average_resolve_times{$period}{'_all_count'};
+ $average_resolve_times{$period}{'_all'} = Time::Duration::concise(Time::Duration::duration($plot{$period}{Average}));
+}
+
+</%init>
diff --git a/rt/html/Reports/Activity/index.html b/rt/html/Reports/Activity/index.html
new file mode 100644
index 000000000..1f6ddb0d5
--- /dev/null
+++ b/rt/html/Reports/Activity/index.html
@@ -0,0 +1,29 @@
+<&| Elements/Wrapper, %ARGS, title => loc("Activity reports"), show_print_link => 0 &>
+
+
+</&>
+
+<%args>
+$type => undef
+$start => undef
+$end => undef
+$query => "Status = 'resolved'"
+</%args>
+<%init>
+
+unless ($start) {
+ my $then = RT::Date->new($session{'CurrentUser'});
+ $then->Set(Format => 'Unix', Value => time - (86400*7));
+ $ARGS{start} = substr($then->ISO,0,10);
+}
+
+unless ($end) {
+ my $now = RT::Date->new($session{'CurrentUser'});
+ $now->SetToNow();
+ $ARGS{end} = substr($now->ISO,0,10);
+}
+
+if ($type) {
+ $m->redirect($type . ".html?" . $m->comp('/Elements/QueryString', query => $query, start => $start, end => $end));
+}
+</%init>
diff --git a/rt/html/Search/Elements/PickRestriction b/rt/html/Search/Elements/PickRestriction
deleted file mode 100644
index ff9b86ba5..000000000
--- a/rt/html/Search/Elements/PickRestriction
+++ /dev/null
@@ -1,142 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<FORM ACTION="<%$RT::WebPath%>/Search/Listing.html" METHOD="GET">
-<INPUT TYPE=HIDDEN NAME="Bookmark" VALUE="<% $session{'tickets'}->FreezeLimits()%>">
-<& /Elements/TitleBoxStart, title => loc('Refine search')&>
-<INPUT TYPE=HIDDEN NAME="CompileRestriction" VALUE=1>
-
-<ul>
-<li><&|/l&>Owner is</&> <& /Elements/SelectBoolean, Name => "OwnerOp",
- TrueVal=> '=',
- FalseVal => '!='
-&>
-<& /Elements/SelectOwner, Name => "ValueOfOwner" &>
-
-<li>
-<& /Elements/SelectWatcherType, Name => "WatcherRole", AllowNull => 0 &>
-<&|/l&>email address</&>
-<& /Elements/SelectMatch, Name => "WatcherRoleOp" &>
-<INPUT Name="ValueOfWatcherRole" SIZE=20>
-
-<li>
-<&|/l&>Subject</&> <& /Elements/SelectMatch, Name => "SubjectOp" &>
-<INPUT Name="ValueOfSubject" SIZE=20>
-
-<li><&|/l&>Queue</&> <& /Elements/SelectBoolean, Name => "QueueOp" ,
- True => loc("is"),
- False => loc("isn't"),
- TrueVal=> '=',
- FalseVal => '!=' &>
-<& /Elements/SelectQueue, Name => "ValueOfQueue" &>
-
-
-<li><&|/l&>Priority</&> <& /Elements/SelectEqualityOperator, Name => "PriorityOp" &>
-
-<INPUT Name="ValueOfPriority" SIZE=5>
-
-<li>
-<& /Elements/SelectDateType, Name => 'DateType' &>
-<& /Elements/SelectDateRelation, Name=>"DateOp" &>
-<& /Elements/SelectDate, Name => "ValueOfDate", ShowTime => 0, Default => '' &>
-
-<li><&|/l&>Ticket attachment</&>
-
-<& /Elements/SelectAttachmentField, Name => 'AttachmentField' &>
-<& /Elements/SelectBoolean, Name => "AttachmentFieldOp",
- True => loc("matches"),
- False => loc("does not match"),
- TrueVal => 'LIKE',
- FalseVal => 'NOT LIKE'
-&>
-<Input Name="ValueOfAttachmentField" Size=20>
-
-<li><&|/l&>Status</&>
-<& /Elements/SelectBoolean, Name => "StatusOp",
- True => loc("is"),
- False => loc("isn't"),
- TrueVal=> '=',
- FalseVal => '!='
-&>
-<& /Elements/SelectStatus, Name => "ValueOfStatus", SkipDeleted => 1 &>
-
-
-% while ( my $CustomField = $CustomFields->Next ) {
-
-<li><% $CustomField->Name %>
- <& /Elements/SelectCustomFieldOperator, Name => "CustomFieldOp". $CustomField->id,
- True => loc("is"),
- False => loc("isn't"),
- TrueVal=> '=', FalseVal => '!=' &>
-
-<& /Elements/SelectCustomFieldValue, Name => "CustomField".$CustomField->id,
- CustomField => $CustomField,
- &>
-% }
-
-</UL>
-
-<& /Elements/TitleBoxEnd &>
-
-<& /Elements/TitleBoxStart, title => loc('Ordering and sorting')&>
-
-<UL>
-
-<li><&|/l&>Results per page</&> <& /Elements/SelectResultsPerPage, Name => "RowsPerPage",
- Default => $session{'tickets_rows_per_page'} || '50'
-&>
-
-<li><&|/l&>Sort results by</&> <& /Elements/SelectTicketSortBy, Name => "TicketsSortBy",
- Default => $session{'tickets_sort_by'}
-&>
-<& /Elements/SelectSortOrder, Name => 'TicketsSortOrder', Default => $session{'tickets_sort_order'} &>
-
-<li><input type="checkbox" name="HideResults" <%$ARGS{'HideResults'} && 'CHECKED'%>> <&|/l&>Don't show search results</&>
-<li><& /Elements/Refresh, Name => 'RefreshSearchInterval' , Default => $session{'tickets_refresh_interval'} &>
-
-</UL>
-
-
-</DIV>
-
-
-
-<& /Elements/TitleBoxEnd &>
-
-<& /Elements/Submit, Label => loc('Search'), Name => 'Action'&>
-
-</FORM>
-
-
- <%INIT>
-my $CustomFields = RT::CustomFields->new( $session{'CurrentUser'});
- foreach ( $session{'tickets'}->RestrictionValues('Queue') ) {
- # Gotta load up the $queue object, since queues get stored by name now.
- my $queue = RT::Queue->new($session{'CurrentUser'});
- $queue->Load($_);
- $CustomFields->LimitToQueue($queue->Id);
- }
-
- $CustomFields->LimitToGlobal();
-
-</%INIT>
diff --git a/rt/html/Search/Elements/TicketHeader b/rt/html/Search/Elements/TicketHeader
deleted file mode 100644
index ed2f60e4e..000000000
--- a/rt/html/Search/Elements/TicketHeader
+++ /dev/null
@@ -1,40 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<TR>
-<& TicketHeaderCell , Attribute => 'id', Header => '#'&>
-<& TicketHeaderCell , Attribute => 'Subject'&>
-<& TicketHeaderCell , Attribute => 'Status'&>
-<& TicketHeaderCell , Attribute => 'Queue'&>
-<& TicketHeaderCell , Attribute => 'Owner'&>
-<& TicketHeaderCell , Attribute => 'Priority'&>
-</TR>
-<TR>
-<TH class="ticketheader">&nbsp;</TH>
-<& TicketHeaderCell , Attribute => 'Requestor(s)'&>
-<& TicketHeaderCell , Attribute => 'Created'&>
-<& TicketHeaderCell , Attribute => 'Told', Header => 'Last Contact'&>
-<& TicketHeaderCell , Attribute => 'LastUpdated', Header => 'Last Updated'&>
-<& TicketHeaderCell , Attribute => 'TimeLeft', Header => 'Left'&>
-</TR>
-%# loc('Last Notified');
diff --git a/rt/html/Search/Elements/TicketHeaderCell b/rt/html/Search/Elements/TicketHeaderCell
deleted file mode 100644
index 5def9ea37..000000000
--- a/rt/html/Search/Elements/TicketHeaderCell
+++ /dev/null
@@ -1,55 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<%INIT>
-my ($order,$curorder);
- $Attribute =~ s/Obj->(Name|AsString|AgeAsString)//g;
- if ($session{'tickets_sort_order'} =~ /^asc$/i) {
- $order = 'DESC';
- $curorder = 'ASC';
- } else {
- $order = 'ASC';
- $curorder = 'DESC';
- }
-$Header = $Attribute unless ($Header);
-
-</%INIT>
-<th class="ticketheader">
-% if (grep (/^$Attribute$/i, $session{'tickets'}->SortFields)) {
-<A
-% if ($Attribute eq $session{'tickets_sort_by'}) {
-class="currenttab"
-HREF="<% $RT::WebPath%>/Search/Listing.html?Bookmark=<%$session{'tickets'}->FreezeLimits()|u%>&TicketsSortBy=<%$Attribute%>&TicketsSortOrder=<%$order%>&RowsPerPage=<%$session{'tickets_rows_per_page'}%>">
-% } else {
-HREF="<% $RT::WebPath%>/Search/Listing.html?Bookmark=<%$session{'tickets'}->FreezeLimits()|u%>&TicketsSortBy=<%$Attribute%>&TicketsSortOrder=<%$curorder%>&RowsPerPage=<%$session{'tickets_rows_per_page'}%>">
-% }
-<% loc($Header) %>
-</A>
-% } else {
-<% loc($Header) %>
-% }
-</th>
-<%ARGS>
-$Header => undef
-$Attribute => undef
-</%ARGS>
diff --git a/rt/html/Search/Elements/TicketRow b/rt/html/Search/Elements/TicketRow
deleted file mode 100644
index 5d1ad209a..000000000
--- a/rt/html/Search/Elements/TicketRow
+++ /dev/null
@@ -1,55 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<SPAN class="search">
-<TR
-% if ($i%2) {
-CLASS="oddline"
-% } else {
-CLASS="evenline"
-% }
->
-<TD ROWSPAN="2"><B><A HREF="<%$RT::WebPath%>/Ticket/Display.html?id=<%$Ticket->Id%>"><%$Ticket->id%></a></B></TD>
-<TD><B><A HREF="<%$RT::WebPath%>/Ticket/Display.html?id=<%$Ticket->Id%>"><%$Ticket->Subject%></a></B></TD>
-<TD><%loc($Ticket->Status)%></TD>
-<TD><%$Ticket->QueueObj->Name%></TD>
-<TD><%$Ticket->Owner == $RT::Nobody->Id ? loc('Nobody') : $Ticket->OwnerObj->Name%></TD>
-<TD><%$Ticket->Priority%></TD>
-</TR>
-<TR
-% if ($i%2) {
-CLASS="oddline"
-% } else {
-CLASS="evenline"
-% }
-><TD><small><%$Ticket->Requestors->MemberEmailAddressesAsString%></small></TD>
-<TD><SMALL><%$Ticket->CreatedObj->AgeAsString || '-'%></SMALL></TD>
-<TD><SMALL><%$Ticket->ToldObj->AgeAsString || '-'%></SMALL></TD>
-<TD><SMALL><%$Ticket->LastUpdatedObj->AgeAsString || '-'%></SMALL></TD>
-<TD><SMALL><%$Ticket->TimeLeft%></SMALL></TD>
-</TR>
-</SPAN>
-<%ARGS>
-$Ticket => undef
-$i => undef
-</%ARGS>
diff --git a/rt/html/Search/Listing.html b/rt/html/Search/Listing.html
deleted file mode 100644
index 68b1fd75c..000000000
--- a/rt/html/Search/Listing.html
+++ /dev/null
@@ -1,113 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<& /Elements/Header, Title => $title, Refresh => $session{'tickets_refresh_interval'} &>
-<& /Ticket/Elements/Tabs,
- current_tab => 'Search/Listing.html',
- Title => $title &>
-
-%if ($ticketcount && ! $ARGS{'HideResults'}) {
-<TABLE WIDTH=100% border=0 cellpadding=2 CELLSPACING=0>
-<& Elements/TicketHeader, %ARGS &>
-% my $i;
-%while (my $Ticket = $session{'tickets'}->Next) {
-% $i++;
-<& Elements/TicketRow, Ticket => $Ticket, i=> $i, %ARGS &>
-%}
-</TABLE>
-<div align=center>
-<font size=2>
-<a href="<%$RT::WebPath%>/Search/Listing.html?GotoPage=1"><&|/l&>First page</&></a>
-&nbsp;&nbsp;
-% if ( $session{'tickets'}->FirstRow >= $session{'tickets_rows_per_page'}-1 ) {
-<a href="<%$RT::WebPath%>/Search/Listing.html?GotoPage=Prev">&lt;<&|/l&>Previous page</&></a>
-&nbsp;&nbsp;
-% }
-% if ( $session{'tickets'}->FirstRow + $session{'tickets_rows_per_page'} < $ticketcount ) {
-<a href="<%$RT::WebPath%>/Search/Listing.html?GotoPage=Next"><&|/l&>Next page</&>&gt;</a>
-% }
-%#&nbsp;&nbsp;<form method=get action="<%$RT::WebPath%>/Search/Listing.html"><&|/l&>Goto page</&> <input name=GotoPage size=2></form>
-</font>
-</div>
-<!--<div align=right>-->
-<table width="100%" border=0 cellpadding=3 CELLSPACING=1>
-<tr>
-<td align=left>
-(<&|/l, ($session{'tickets'}->FirstRow+1), ($session{'tickets'}->FirstRow() + $session{'tickets'}->RowsPerPage() ) &>[_1] - [_2] shown</&>)
-</td>
-<td align=right>
-
-<a href="<%$RT::WebPath%>/Search/Bulk.html"><&|/l&>Update all these tickets at once</&></a>
-<!--</div>-->
-</td>
-</tr>
-</table>
-
-% }
-<TABLE WIDTH="100%">
-<TR>
-<TD VALIGN="TOP">
-<& /Elements/TitleBoxStart, title => loc('Current search criteria')&>
-
-%my %restrictions=$session{'tickets'}->DescribeRestrictions();
-%foreach my $row (keys %restrictions){
-<%$restrictions{"$row"}%> <A HREF="<% $RT::WebPath %>/Search/Listing.html?DeleteRestriction=<%$row%>">[<&|/l&>delete</&>]</a><br>
-%}
-<BR>
-<BR>
-<A HREF="<% $RT::WebPath%>/Search/Listing.html?Bookmark=<%$session{'tickets'}->FreezeLimits()|nu%>&TicketsSortBy=<%$session{'tickets_sort_by'}%>&TicketsSortOrder=<%$session{'tickets_sort_order'}%>&RowsPerPage=<%$session{'tickets_rows_per_page'}%>"><&|/l&>Bookmarkable URL for this search</&></a>
-<& /Elements/TitleBoxEnd&>
-</TD>
-<TD>
-
-<& Elements/PickRestriction, %ARGS &>
-
-</TD>
-</TR>
-</TABLE>
-
-<%INIT>
-
-my ($title, $ticketcount);
-$session{'i'}++;
-if ($session{'tickets'}) {
- if ($ARGS{'DeleteRestriction'}) {
- $session{'tickets'}->DeleteRestriction($ARGS{'DeleteRestriction'});
- }
- if ( ($ARGS{'ClearRestrictions'}) || ($ARGS{'NewSearch'}) ) {
- $session{'tickets'}->ClearRestrictions;
- $session{'tickets'}->CleanSlate;
- }
-}
- ProcessSearchQuery(ARGS=>\%ARGS);
- $session{'tickets'}->RedoSearch();
- if ( $session{'tickets'}->DescribeRestrictions()) {
- $ticketcount = $session{tickets}->CountAll();
- $title = loc('Found [quant,_1,ticket]', $ticketcount);
- } else {
- $title = loc("Find tickets");
- }
-</%INIT>
-<%CLEANUP>
-$session{'tickets'}->PrepForSerialization();
-</%CLEANUP>
diff --git a/rt/html/Ticket/Create.html b/rt/html/Ticket/Create.html
index c35ed9122..9b38d2608 100644
--- a/rt/html/Ticket/Create.html
+++ b/rt/html/Ticket/Create.html
@@ -388,10 +388,10 @@ if ((!exists $ARGS{'AddMoreAttach'}) and ($ARGS{'id'} eq 'new')) { # new ticket?
my $actions = {
A => {
- html => q[<a href="#basics" onclick="return switchVisibility('Ticket-Create-basics','Ticket-Create-details');">] . loc('Show basics') . q[</a>],
+ html => q[<a href="#basics" onclick="return switchVisibility('Ticket-Create-basics','Ticket-Create-details');" CLASS="fsblackbutton">] . loc('Show basics') . q[</a>],
},
B => {
- html => q[<a href="#details" onclick="return switchVisibility('Ticket-Create-details','Ticket-Create-basics');">] . loc('Show details') . q[</a>],
+ html => q[<a href="#details" onclick="return switchVisibility('Ticket-Create-details','Ticket-Create-basics');" CLASS="fsblackbutton">] . loc('Show details') . q[</a>],
},
};
</%INIT>
diff --git a/rt/html/Ticket/Display.html b/rt/html/Ticket/Display.html
index 7bdd57f93..d0422c008 100644
--- a/rt/html/Ticket/Display.html
+++ b/rt/html/Ticket/Display.html
@@ -46,11 +46,11 @@
%#
%# END BPS TAGGED BLOCK }}}
<& /Elements/Header,
- Title => loc("#[_1]: [_2]", $TicketObj->Id, $TicketObj->Subject) &>
+ Title => loc("Ticket #[_1]: [_2]", $TicketObj->Id, $TicketObj->Subject) &>
<& /Ticket/Elements/Tabs,
Ticket => $TicketObj,
current_tab => 'Ticket/Display.html?id='.$TicketObj->id,
- Title => loc("#[_1]: [_2]", $TicketObj->Id, $TicketObj->Subject) &>
+ Title => loc("Ticket #[_1]: [_2]", $TicketObj->Id, $TicketObj->Subject) &>
<& /Elements/ListActions, actions => \@Actions &>
<& /Elements/Callback, _CallbackName => 'BeforeShowSummary', Ticket => $TicketObj, %ARGS &>
diff --git a/rt/html/Ticket/Elements/AddCustomers b/rt/html/Ticket/Elements/AddCustomers
new file mode 100644
index 000000000..e04c07702
--- /dev/null
+++ b/rt/html/Ticket/Elements/AddCustomers
@@ -0,0 +1,52 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%# Copyright (c) 2008 Freeside Internet Services, Inc.
+%#
+%# 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.
+<BR>
+<%$msg%><br>
+
+% if (@Customers) {
+
+<br><i>(Check box to link)<i>
+<table>
+% foreach my $customer (@Customers) {
+<tr>
+ <td>
+ <input type="checkbox" name="Ticket-AddCustomer-<% $customer->{'custnum'} %>" VALUE="1" <% scalar(@Customers) == 1 ? 'CHECKED' : '' %>>
+ <A HREF="<%$freeside_url%>/view/cust_main.cgi?<% $customer->{'custnum'} %>"><% &RT::URI::freeside::small_custview($customer->{'custnum'}, &RT::URI::freeside::FreesideGetConfig('countrydefault'), 1) |n %>
+ </td>
+</tr>
+% }
+</table>
+
+% }
+
+<%INIT>
+my ($msg);
+
+my $freeside_url = &RT::URI::freeside::FreesideURL();
+
+my @Customers = ();
+if ( $CustomerString ) {
+ @Customers = &RT::URI::freeside::smart_search( 'search' => $CustomerString );
+}
+
+my @Services = ();
+if ($ServiceString) {
+ @Services = (); #service_search();
+}
+
+</%INIT>
+
+<%ARGS>
+$CustomerString => undef
+$ServiceString => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/EditCustomers b/rt/html/Ticket/Elements/EditCustomers
new file mode 100644
index 000000000..0ba6e447b
--- /dev/null
+++ b/rt/html/Ticket/Elements/EditCustomers
@@ -0,0 +1,63 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%# Copyright (c) 2008 Freeside Internet Services, Inc.
+%#
+%# 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.
+<TABLE width=100%>
+ <TR>
+ <TD VALIGN=TOP WIDTH=50%>
+ <h3><&|/l&>Current Customers</&></h3>
+
+<table>
+ <tr>
+ <td><i><&|/l&>(Check box to disassociate)</&></i></td>
+ </tr>
+ <tr>
+ <td class="value">
+% foreach my $link ( @{ $Ticket->Customers->ItemsArrayRef } ) {
+
+ <INPUT TYPE=CHECKBOX NAME="DeleteLink--<%$link->Type%>-<%$link->Target%>">
+%# <& ShowLink, URI => $link->TargetURI &><br>
+ <A HREF="<% $link->TargetURI->Resolver->HREF %>"><% $link->TargetURI->Resolver->AsStringLong |n %></A>
+ <BR>
+% }
+ </td>
+ </tr>
+</table>
+
+</TD>
+
+<TD VALIGN=TOP>
+<h3><&|/l&>New Customer Links</&></h3>
+<&|/l&>Find customer</&><BR>
+<input name="CustomerString">
+<input type=submit name="OnlySearchForCustomers" value="<&|/l&>Go!</&>">
+<br><i>cust #, name, company or phone</i>
+<BR>
+%#<BR>
+%#<&|/l&>Find service</&><BR>
+%#<input name="ServiceString">
+%#<input type=submit name="OnlySearchForServices" value="<&|/l&>Go!</&>">
+%#<br><i>username, username@domain, domain, or IP address</i>
+%#<BR>
+
+<& AddCustomers, Ticket => $Ticket,
+ CustomerString => $CustomerString,
+ ServiceString => $ServiceString, &>
+
+</TD>
+</TR>
+</TABLE>
+
+<%ARGS>
+$CustomerString => undef
+$ServiceString => undef
+$Ticket => undef
+</%ARGS>
diff --git a/rt/html/Ticket/Elements/EditLinks b/rt/html/Ticket/Elements/EditLinks
deleted file mode 100644
index bdb8a6b7d..000000000
--- a/rt/html/Ticket/Elements/EditLinks
+++ /dev/null
@@ -1,133 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<TABLE width=100%>
- <TR>
- <TD VALIGN=TOP WIDTH=50%>
- <h3><&|/l&>Current Relationships</&></h3>
-
-<table>
- <tr>
- <td></td>
- <td><i><&|/l&>(Check box to delete)</&></i></td>
- </tr>
- <tr>
- <td class="labeltop"><&|/l&>Depends on</&>:</td>
- <td class="value">
-% while (my $link = $Ticket->DependsOn->Next) {
- <INPUT TYPE=CHECKBOX NAME="DeleteLink--<%$link->Type%>-<%$link->Target%>">
- <& ShowLink, URI => $link->TargetURI &><br>
-% }
- </td>
- </tr>
- <tr>
- <td class="labeltop"><&|/l&>Depended on by</&>:</td>
- <td class="value">
-% while (my $link = $Ticket->DependedOnBy->Next) {
-% my $member = $link->BaseObj;
- <INPUT TYPE=CHECKBOX NAME="DeleteLink-<%$link->Base%>-<%$link->Type%>-">
- <& ShowLink, URI => $link->BaseURI &><br>
-% }
- </td>
- </tr>
- <tr>
- <td class="labeltop"><&|/l&>Parents</&>:</td>
- <td class="value">
-% while (my $link = $Ticket->MemberOf->Next) {
- <INPUT TYPE=CHECKBOX NAME="DeleteLink--<%$link->Type%>-<%$link->Target%>">
- <& ShowLink, URI => $link->TargetURI &><br>
-% }
- </td>
- </tr>
- <tr>
- <td class="labeltop"><&|/l&>Children</&>:</td>
- <td class="value">
-% while (my $link = $Ticket->Members->Next) {
- <INPUT TYPE=CHECKBOX NAME="DeleteLink-<%$link->Base%>-<%$link->Type%>-">
- <& ShowLink, URI => $link->BaseURI &><br>
-% }
- </td>
- </tr>
- <tr>
- <td class="labeltop"><&|/l&>Refers to</&>:</td>
- <td class="value">
-% while (my $link = $Ticket->RefersTo->Next) {
- <INPUT TYPE=CHECKBOX NAME="DeleteLink--<%$link->Type%>-<%$link->Target%>">
- <& ShowLink, URI => $link->TargetURI &><br>
-%}
- </td>
- </tr>
- <tr>
- <td class="labeltop"><&|/l&>Referred to by</&>:</td>
- <td class="value">
-% while (my $link = $Ticket->ReferredToBy->Next) {
- <INPUT TYPE=CHECKBOX NAME="DeleteLink-<%$link->Base%>-<%$link->Type%>-">
- <& ShowLink, URI => $link->BaseURI &><br>
-% }
- </td>
- </tr>
-</table>
-
-</TD>
-<TD VALIGN=TOP>
-<h3><&|/l&>New Relationships</&></h3>
-<i><&|/l&>Enter tickets or URIs to link tickets to. Seperate multiple entries with spaces.</&></i><br>
-<TABLE>
- <TR>
- <TD class="label"><&|/l&>Merge into</&>:</TD>
- <TD class="entry"><input name="<%$Ticket->Id%>-MergeInto"> <i><&|/l&>(only one ticket)</&></i></TD>
- </TR>
- <TR>
- <TD class="label"><&|/l&>Depends on</&>:</TD>
- <TD class="entry"><input name="<%$Ticket->Id%>-DependsOn"></TD>
- </TR>
- <TR>
- <TD class="label"><&|/l&>Depended on by</&>:</TD>
- <TD class="entry"><input name="DependsOn-<%$Ticket->Id%>"></TD>
- </TR>
- <TR>
- <TD class="label"><&|/l&>Parents</&>:</TD>
- <TD class="entry"><input name="<%$Ticket->Id%>-MemberOf"></TD>
- </TR>
- <TR>
- <TD class="label"><&|/l&>Children</&>:</TD>
- <TD class="entry"> <input name="MemberOf-<%$Ticket->Id%>"></TD>
- </TR>
- <TR>
- <TD class="label"><&|/l&>Refers to</&>:</TD>
- <TD class="entry"><input name="<%$Ticket->Id%>-RefersTo"></TD>
- </TR>
- <TR>
- <TD class="label"><&|/l&>Referred to by</&>:</TD>
- <TD class="entry"> <input name="RefersTo-<%$Ticket->Id%>"></TD>
- </TR>
-</TABLE>
-</TD>
-</TR>
-</TABLE>
-
-
-
-<%ARGS>
-$Ticket => undef
-</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowCustomers b/rt/html/Ticket/Elements/ShowCustomers
new file mode 100644
index 000000000..3acf92dd4
--- /dev/null
+++ b/rt/html/Ticket/Elements/ShowCustomers
@@ -0,0 +1,38 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%#
+%# 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.
+<table>
+% my $cust = 0;
+% foreach my $custResolver ( map { $_->TargetURI->Resolver }
+% @{ $Ticket->Customers->ItemsArrayRef }
+% )
+% {
+% $cust++;
+% my $cust_main = '';
+ <tr>
+ <td class="value">
+ <A HREF="<% $custResolver->HREF %>"><% $custResolver->AsStringLong |n %></A>
+ </td>
+ </tr>
+% }
+% unless ( $cust ) {
+ <tr>
+ <td class="labeltop">
+ <i>(none)<i>
+ </td>
+ </tr>
+
+% }
+</table>
+<%ARGS>
+$Ticket => undef
+</%ARGS>
+
diff --git a/rt/html/Ticket/Elements/ShowLink b/rt/html/Ticket/Elements/ShowLink
deleted file mode 100644
index 493fd95a5..000000000
--- a/rt/html/Ticket/Elements/ShowLink
+++ /dev/null
@@ -1,40 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<A href="<%$URI->Resolver->HREF%>">
-% if ($URI->IsLocal) {
-% my $member = $URI->Object;
-% if (UNIVERSAL::isa($member, "RT::Ticket")) {
-<%$member->Id%>: (<%$member->OwnerObj->Name%>) <%$member->Subject%> [<% loc($member->Status) %>]
-% } elsif ( UNIVERSAL::can($member, 'Name')) {
-<%$URI->Resolver->AsString%>: <%$member->Name%>
-% } else {
-<%$URI->Resolver->AsString%>
-% }
-% } else {
-<%$URI->Resolver->AsString%>
-% }
-</a>
-<%ARGS>
-$URI => undef
-</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowLinks b/rt/html/Ticket/Elements/ShowLinks
deleted file mode 100644
index f88a6008d..000000000
--- a/rt/html/Ticket/Elements/ShowLinks
+++ /dev/null
@@ -1,87 +0,0 @@
-%# BEGIN LICENSE BLOCK
-%#
-%# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
-<table>
- <tr>
- <td class="labeltop"><&|/l&>Depends on</&>:</td>
- <td class="value">
-<ul>
-% while (my $Link = $Ticket->DependsOn->Next) {
-<li><& ShowLink, URI => $Link->TargetURI &>
-% }
-</ul>
- </td>
- </tr>
- <tr>
- <td class="labeltop"><&|/l&>Depended on by</&>:</td>
- <td class="value">
-<ul>
-% while (my $Link = $Ticket->DependedOnBy->Next) {
-<li><& ShowLink, URI => $Link->BaseURI &>
-% }
-</ul>
- </td>
- </tr>
- <tr>
- <td class="labeltop"><&|/l&>Parents</&>:</td>
- <td class="value">
-<ul>
-% while (my $Link = $Ticket->MemberOf->Next) {
-<li><& ShowLink, URI => $Link->TargetURI &>
-% }
-</ul>
- </td>
- </tr>
- <tr>
- <td class="labeltop"><&|/l&>Children</&>:</td>
- <td class="value"><& /Ticket/Elements/ShowMembers, Ticket => $Ticket &></td>
- </tr>
- <tr>
- <td class="labeltop"><&|/l&>Refers to</&>:</td>
- <td class="value">
-<ul>
-% while (my $Link = $Ticket->RefersTo->Next) {
-<li><& ShowLink, URI => $Link->TargetURI &>
-% }
-</ul>
- </td>
- </tr>
- <tr>
- <td class="labeltop"><&|/l&>Referred to by</&>:</td>
- <td class="value">
- <ul>
-% while (my $Link = $Ticket->ReferredToBy->Next) {
-<li><& ShowLink, URI => $Link->BaseURI &>
-% }
-</ul>
- </td>
- </tr>
-
-% # Allow people to add more rows to the table
-% $m->comp('/Elements/Callback', %ARGS );
-
-</table>
-
-<%ARGS>
-$Ticket => undef
-</%ARGS>
diff --git a/rt/html/Ticket/Elements/ShowSummary b/rt/html/Ticket/Elements/ShowSummary
index aeec0fdfb..2f153f3bc 100644
--- a/rt/html/Ticket/Elements/ShowSummary
+++ b/rt/html/Ticket/Elements/ShowSummary
@@ -67,6 +67,12 @@
<& /Ticket/Elements/ShowPeople, Ticket => $Ticket &>
</&>
+ <&| /Widgets/TitleBox, title => loc('Customers'),
+ title_href =>"$RT::WebPath/Ticket/ModifyCustomers.html?id=".$Ticket->Id,
+ class=> 'ticket-info-customers' &>
+ <& /Ticket/Elements/ShowCustomers, Ticket => $Ticket &>
+ </&>
+
<& /Ticket/Elements/ShowAttachments, Ticket => $Ticket, Attachments => $Attachments &>
<br />
<& /Ticket/Elements/ShowRequestor, Ticket => $Ticket &>
diff --git a/rt/html/Ticket/Elements/ShowTransactionAttachments b/rt/html/Ticket/Elements/ShowTransactionAttachments
index 662b744ae..66823d743 100644
--- a/rt/html/Ticket/Elements/ShowTransactionAttachments
+++ b/rt/html/Ticket/Elements/ShowTransactionAttachments
@@ -139,8 +139,14 @@ unless ( ($message->GetHeader('Content-Disposition')||"") =~ /attachment/i ) {
# if it's a text/plain show the body
elsif ( $message->ContentType =~ m{^(text|message|text)}i ) {
- eval { require Text::Quoted; $content = Text::Quoted::extract($content); };
- if ($@) { 1; }
+ #don't want to use this even if it is installed, its
+ #segfaulting on weird characters and silently truncating the
+ #ticket history output
+ #see:
+ # r44838@pinglin: jesse | 2006-11-14 15:53:18 -0500
+ # * Move Text::Quoted back to being a run-time require. So that it's possible to turn off the feature if it causes your perl to segfault. (Text::Tabs is...not robust in the face of perl bugs)
+ #eval { require Text::Quoted; $content = Text::Quoted::extract($content); };
+ #if ($@) { 1; }
$m->comp(
'ShowMessageStanza',
diff --git a/rt/html/Ticket/Elements/Tabs b/rt/html/Ticket/Elements/Tabs
index 98ed143e9..3149e6b7c 100644
--- a/rt/html/Ticket/Elements/Tabs
+++ b/rt/html/Ticket/Elements/Tabs
@@ -121,6 +121,8 @@ my $ticket_page_tabs = {
{ title => loc('People'), path => "Ticket/ModifyPeople.html?id=" . $id, },
_E => { title => loc('Links'),
path => "Ticket/ModifyLinks.html?id=" . $id, },
+ _Eb=> { title => loc('Customers'),
+ path => "Ticket/ModifyCustomers.html?id=" . $id, },
_F => { title => loc('Reminders'),
path => "Ticket/Reminders.html?id=" . $id,
separator => 1, },
diff --git a/rt/html/Ticket/ModifyCustomers.html b/rt/html/Ticket/ModifyCustomers.html
new file mode 100644
index 000000000..72d103b23
--- /dev/null
+++ b/rt/html/Ticket/ModifyCustomers.html
@@ -0,0 +1,49 @@
+%# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+%#
+%# 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.
+<& /Elements/Header, Title => loc("Customers for ticket #[_1]", $Ticket->Id) &>
+<& /Ticket/Elements/Tabs,
+ Ticket => $Ticket,
+ current_tab => "Ticket/ModifyCustomers.html?id=".$Ticket->Id,
+ Title => loc("Customers for ticket #[_1]", $Ticket->Id) &>
+
+<& /Elements/ListActions, actions => \@results &>
+
+<form action="ModifyCustomers.html" method="post">
+<input type="hidden" name="id" value="<%$Ticket->id%>">
+
+<& /Elements/TitleBoxStart, title => loc('Edit Customer Links'), color => "#7f007b"&>
+<& Elements/EditCustomers, Ticket => $Ticket, CustomerString => $CustomerString, ServiceString => $ServiceString &>
+<& /Elements/TitleBoxEnd &>
+<& /Elements/Submit, color => "#7f007b", Label => loc('Save Changes') &>
+</form>
+
+
+<%INIT>
+
+my @results = ();
+my $Ticket = LoadTicket($id);
+
+# if we're trying to search for customers/services and nothing else
+unless ( $OnlySearchForCustomers || $OnlySearchForServices) {
+ @results = ProcessTicketCustomers( TicketObj => $Ticket, ARGSRef => \%ARGS);
+}
+
+</%INIT>
+
+
+<%ARGS>
+$OnlySearchForCustomers => undef
+$OnlySearchForServices => undef
+$CustomerString => undef
+$ServiceString => undef
+$id => undef
+</%ARGS>
diff --git a/rt/html/User/Prefs.html b/rt/html/User/Prefs.html
index 54a950d7c..56d2ee05b 100644
--- a/rt/html/User/Prefs.html
+++ b/rt/html/User/Prefs.html
@@ -45,7 +45,7 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<& /Elements/Header, Title=>loc("Preferences") &>
+<& /Elements/Header, Title=>loc("Ticketing Preferences") &>
<& /User/Elements/Tabs,
current_tab => 'User/Prefs.html',
Title=>loc("Preferences") &>
diff --git a/rt/lib/RT.pm b/rt/lib/RT.pm
index 3ed85afc0..033e5e607 100644
--- a/rt/lib/RT.pm
+++ b/rt/lib/RT.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,79 +45,61 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
-use strict;
-use warnings;
-
package RT;
-
-
-use File::Spec ();
-use Cwd ();
-
-use vars qw($Config $System $SystemUser $Nobody $Handle $Logger $_INSTALL_MODE);
-
-our $VERSION = '3.8.7';
-
-
-
-our $BasePath = '/opt/rt3';
-our $EtcPath = 'etc';
-our $BinPath = 'bin';
-our $SbinPath = 'sbin';
-our $VarPath = 'var';
-our $PluginPath = 'plugins';
-our $LocalPath = 'local';
-our $LocalEtcPath = 'local/etc';
-our $LocalLibPath = 'local/lib';
-our $LocalLexiconPath = 'local/po';
-our $LocalPluginPath = $LocalPath."/plugins";
-
+use strict;
+use RT::I18N;
+use RT::CurrentUser;
+use RT::System;
+
+use vars qw($VERSION $System $SystemUser $Nobody $Handle $Logger
+ $CORE_CONFIG_FILE
+ $SITE_CONFIG_FILE
+ $BasePath
+ $EtcPath
+ $VarPath
+ $LocalPath
+ $LocalEtcPath
+ $LocalLexiconPath
+ $LogDir
+ $BinPath
+ $MasonComponentRoot
+ $MasonLocalComponentRoot
+ $MasonDataDir
+ $MasonSessionDir
+);
+
+$VERSION = '3.6.10';
+$CORE_CONFIG_FILE = "/opt/rt3/etc/RT_Config.pm";
+$SITE_CONFIG_FILE = "/opt/rt3/etc/RT_SiteConfig.pm";
+
+
+
+$BasePath = '/opt/rt3';
+
+$EtcPath = '/opt/rt3/etc';
+$BinPath = '/opt/rt3/bin';
+$VarPath = '/opt/rt3/var';
+$LocalPath = '/opt/rt3/local';
+$LocalEtcPath = '/opt/rt3/local/etc';
+$LocalLexiconPath = '/opt/rt3/local/po';
# $MasonComponentRoot is where your rt instance keeps its mason html files
-our $MasonComponentRoot = 'share/html';
+$MasonComponentRoot = '/var/www/freeside/rt';
# $MasonLocalComponentRoot is where your rt instance keeps its site-local
# mason html files.
-our $MasonLocalComponentRoot = 'local/html';
+$MasonLocalComponentRoot = '/opt/rt3/local/html';
# $MasonDataDir Where mason keeps its datafiles
-our $MasonDataDir = 'var/mason_data';
+$MasonDataDir = '/usr/local/etc/freeside/masondata';
# RT needs to put session data (for preserving state between connections
# via the web interface)
-our $MasonSessionDir = 'var/session_data';
-
-unless ( File::Spec->file_name_is_absolute($EtcPath) ) {
-
-# if BasePath exists and is absolute, we won't infer it from $INC{'RT.pm'}.
-# otherwise RT.pm will make src dir(where we configure RT) be the BasePath
-# instead of the --prefix one
- unless ( -d $BasePath && File::Spec->file_name_is_absolute($BasePath) ) {
- my $pm_path = ( File::Spec->splitpath( $INC{'RT.pm'} ) )[1];
+$MasonSessionDir = '/opt/rt3/var/session_data';
- # need rel2abs here is to make sure path is absolute, since $INC{'RT.pm'}
- # is not always absolute
- $BasePath =
- File::Spec->rel2abs(
- File::Spec->catdir( $pm_path, File::Spec->updir ) );
- }
-
- $BasePath = Cwd::realpath( $BasePath );
-
- for my $path ( qw/EtcPath BinPath SbinPath VarPath LocalPath LocalEtcPath
- LocalLibPath LocalLexiconPath PluginPath LocalPluginPath
- MasonComponentRoot MasonLocalComponentRoot MasonDataDir
- MasonSessionDir/ ) {
- no strict 'refs';
- # just change relative ones
- $$path = File::Spec->catfile( $BasePath, $$path )
- unless File::Spec->file_name_is_absolute( $$path );
- }
-}
=head1 NAME
@@ -130,14 +112,12 @@ A fully featured request tracker package
=head1 DESCRIPTION
-=head2 INITIALIZATION
-
=head2 LoadConfig
Load RT's config file. First, the site configuration file
-(F<RT_SiteConfig.pm>) is loaded, in order to establish overall site
+(C<RT_SiteConfig.pm>) is loaded, in order to establish overall site
settings like hostname and name of RT instance. Then, the core
-configuration file (F<RT_Config.pm>) is loaded to set fallback values
+configuration file (C<RT_Config.pm>) is loaded to set fallback values
for all settings; it bases some values on settings from the site
configuration file.
@@ -148,33 +128,59 @@ have not been set already.
=cut
sub LoadConfig {
- require RT::Config;
- $Config = new RT::Config;
- $Config->LoadConfigs;
- require RT::I18N;
+ local *Set = sub { $_[0] = $_[1] unless defined $_[0] };
+
+ my $username = getpwuid($>);
+ my $group = getgrgid($();
+ my $message = <<EOF;
+
+RT couldn't load RT config file %s as:
+ user: $username
+ group: $group
+
+The file is owned by user %s and group %s.
+
+This usually means that the user/group your webserver is running
+as cannot read the file. Be careful not to make the permissions
+on this file too liberal, because it contains database passwords.
+You may need to put the webserver user in the appropriate group
+(%s) or change permissions be able to run succesfully.
+EOF
+
+
+ if ( -f "$SITE_CONFIG_FILE" ) {
+ eval { require $SITE_CONFIG_FILE };
+ if ($@) {
+ my ($fileuid,$filegid) = (stat($SITE_CONFIG_FILE))[4,5];
+ my $fileusername = getpwuid($fileuid);
+ my $filegroup = getgrgid($filegid);
+ my $errormessage = sprintf($message, $SITE_CONFIG_FILE,
+ $fileusername, $filegroup, $filegroup);
+ die ("$errormessage\n$@");
+ }
+ }
+ eval { require $CORE_CONFIG_FILE };
+ if ($@) {
+ my ($fileuid,$filegid) = (stat($CORE_CONFIG_FILE))[4,5];
+ my $fileusername = getpwuid($fileuid);
+ my $filegroup = getgrgid($filegid);
+ my $errormessage = sprintf($message, $CORE_CONFIG_FILE,
+ $fileusername, $filegroup, $filegroup);
+ die ("$errormessage\n$@")
+ }
# RT::Essentials mistakenly recommends that WebPath be set to '/'.
# If the user does that, do what they mean.
$RT::WebPath = '' if ($RT::WebPath eq '/');
- # fix relative LogDir and GnuPG homedir
- unless ( File::Spec->file_name_is_absolute( $Config->Get('LogDir') ) ) {
- $Config->Set( LogDir =>
- File::Spec->catfile( $BasePath, $Config->Get('LogDir') ) );
- }
+ $ENV{'TZ'} = $RT::Timezone if ($RT::Timezone);
- my $gpgopts = $Config->Get('GnuPGOptions');
- unless ( File::Spec->file_name_is_absolute( $gpgopts->{homedir} ) ) {
- $gpgopts->{homedir} = File::Spec->catfile( $BasePath, $gpgopts->{homedir} );
- }
-
RT::I18N->Init;
}
=head2 Init
-L<Connect to the database /ConnectToDatabase>, L<initilizes system objects /InitSystemObjects>,
-L<preloads classes /InitClasses> and L<set up logging /InitLogging>.
+Conenct to the database, set up logging.
=cut
@@ -182,34 +188,41 @@ sub Init {
CheckPerlRequirements();
- InitPluginPaths();
-
#Get a database connection
ConnectToDatabase();
- InitSystemObjects();
+
+ #RT's system user is a genuine database user. its id lives here
+ $SystemUser = new RT::CurrentUser();
+ $SystemUser->LoadByName('RT_System');
+
+ #RT's "nobody user" is a genuine database user. its ID lives here.
+ $Nobody = new RT::CurrentUser();
+ $Nobody->LoadByName('Nobody');
+
+ $System = RT::System->new();
+
InitClasses();
InitLogging();
- InitPlugins();
- RT->Config->PostLoadCheck;
-
}
+
=head2 ConnectToDatabase
-Get a database connection. See also </Handle>.
+Get a database connection
=cut
sub ConnectToDatabase {
require RT::Handle;
- $Handle = new RT::Handle unless $Handle;
- $Handle->Connect;
- return $Handle;
+ unless ($Handle && $Handle->dbh && $Handle->dbh->ping) {
+ $Handle = RT::Handle->new();
+ }
+ $Handle->Connect();
}
=head2 InitLogging
-Create the Logger object and set up signal handlers.
+Create the RT::Logger object.
=cut
@@ -218,141 +231,112 @@ sub InitLogging {
# We have to set the record separator ($, man perlvar)
# or Log::Dispatch starts getting
# really pissy, as some other module we use unsets it.
+
$, = '';
use Log::Dispatch 1.6;
- my %level_to_num = (
- map( { $_ => } 0..7 ),
- debug => 0,
- info => 1,
- notice => 2,
- warning => 3,
- error => 4, 'err' => 4,
- critical => 5, crit => 5,
- alert => 6,
- emergency => 7, emerg => 7,
- );
+ unless ($RT::Logger) {
- unless ( $RT::Logger ) {
+ $RT::Logger = Log::Dispatch->new();
- $RT::Logger = Log::Dispatch->new;
+ my $simple_cb = sub {
+ # if this code throw any warning we can get segfault
+ no warnings;
- my $stack_from_level;
- if ( $stack_from_level = RT->Config->Get('LogStackTraces') ) {
- # if option has old style '\d'(true) value
- $stack_from_level = 0 if $stack_from_level =~ /^\d+$/;
- $stack_from_level = $level_to_num{ $stack_from_level } || 0;
- } else {
- $stack_from_level = 99; # don't log
- }
+ my %p = @_;
- my $simple_cb = sub {
- # if this code throw any warning we can get segfault
- no warnings;
- my %p = @_;
-
- # skip Log::* stack frames
- my $frame = 0;
- $frame++ while caller($frame) && caller($frame) =~ /^Log::/;
- my ($package, $filename, $line) = caller($frame);
-
- $p{'message'} =~ s/(?:\r*\n)+$//;
- return "[". gmtime(time) ."] [". $p{'level'} ."]: "
- . $p{'message'} ." ($filename:$line)\n";
- };
-
- my $syslog_cb = sub {
- # if this code throw any warning we can get segfault
- no warnings;
- my %p = @_;
-
- my $frame = 0; # stack frame index
- # skip Log::* stack frames
- $frame++ while caller($frame) && caller($frame) =~ /^Log::/;
- my ($package, $filename, $line) = caller($frame);
-
- # syswrite() cannot take utf8; turn it off here.
- Encode::_utf8_off($p{message});
-
- $p{message} =~ s/(?:\r*\n)+$//;
- if ($p{level} eq 'debug') {
- return "$p{message}\n";
- } else {
- return "$p{message} ($filename:$line)\n";
- }
- };
+ my $frame = 0; # stack frame index
+ # skip Log::* stack frames
+ $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ );
- my $stack_cb = sub {
- no warnings;
- my %p = @_;
- return $p{'message'} unless $level_to_num{ $p{'level'} } >= $stack_from_level;
-
- require Devel::StackTrace;
- my $trace = Devel::StackTrace->new( ignore_class => [ 'Log::Dispatch', 'Log::Dispatch::Base' ] );
- return $p{'message'} . $trace->as_string;
+ my ($package, $filename, $line) = caller($frame);
+ $p{message} =~ s/(?:\r*\n)+$//;
+ my $str = "[".gmtime(time)."] [".$p{level}."]: $p{message} ($filename:$line)\n";
+ if( $RT::LogStackTraces ) {
+ $str .= "\nStack trace:\n";
# skip calling of the Log::* subroutins
- my $frame = 0;
- $frame++ while caller($frame) && caller($frame) =~ /^Log::/;
- $frame++ while caller($frame) && (caller($frame))[3] =~ /^Log::/;
-
- $p{'message'} .= "\nStack trace:\n";
+ $frame++ while( caller($frame) && (caller($frame))[3] =~ /^Log::/ );
while( my ($package, $filename, $line, $sub) = caller($frame++) ) {
- $p{'message'} .= "\t$sub(...) called at $filename:$line\n";
- }
- return $p{'message'};
- };
-
- if ( $Config->Get('LogToFile') ) {
- my ($filename, $logdir) = (
- $Config->Get('LogToFileNamed') || 'rt.log',
- $Config->Get('LogDir') || File::Spec->catdir( $VarPath, 'log' ),
- );
- if ( $filename =~ m![/\\]! ) { # looks like an absolute path.
- ($logdir) = $filename =~ m{^(.*[/\\])};
- }
- else {
- $filename = File::Spec->catfile( $logdir, $filename );
+ $str .= "\t". $sub ."() called at $filename:$line\n";
}
+ }
+ return $str;
+ };
- unless ( -d $logdir && ( ( -f $filename && -w $filename ) || -w $logdir ) ) {
- # localizing here would be hard when we don't have a current user yet
- die "Log file '$filename' couldn't be written or created.\n RT can't run.";
- }
+ my $syslog_cb = sub {
+ my %p = @_;
+
+ my $frame = 0; # stack frame index
+ # skip Log::* stack frames
+ $frame++ while( caller($frame) && caller($frame) =~ /^Log::/ );
+ my ($package, $filename, $line) = caller($frame);
+
+ # syswrite() cannot take utf8; turn it off here.
+ Encode::_utf8_off($p{message});
- require Log::Dispatch::File;
- $RT::Logger->add( Log::Dispatch::File->new
- ( name=>'file',
- min_level=> $Config->Get('LogToFile'),
- filename=> $filename,
- mode=>'append',
- callbacks => [ $simple_cb, $stack_cb ],
- ));
+ $p{message} =~ s/(?:\r*\n)+$//;
+ if ($p{level} eq 'debug') {
+ return "$p{message}\n"
+ } else {
+ return "$p{message} ($filename:$line)\n"
+ }
+ };
+
+ if ($RT::LogToFile) {
+ my ($filename, $logdir);
+ if ($RT::LogToFileNamed =~ m![/\\]!) {
+ # looks like an absolute path.
+ $filename = $RT::LogToFileNamed;
+ ($logdir) = $RT::LogToFileNamed =~ m!^(.*[/\\])!;
}
- if ( $Config->Get('LogToScreen') ) {
- require Log::Dispatch::Screen;
- $RT::Logger->add( Log::Dispatch::Screen->new
- ( name => 'screen',
- min_level => $Config->Get('LogToScreen'),
- callbacks => [ $simple_cb, $stack_cb ],
- stderr => 1,
- ));
+ else {
+ $filename = "$RT::LogDir/$RT::LogToFileNamed";
+ $logdir = $RT::LogDir;
}
- if ( $Config->Get('LogToSyslog') ) {
- require Log::Dispatch::Syslog;
- $RT::Logger->add(Log::Dispatch::Syslog->new
- ( name => 'syslog',
- ident => 'RT',
- min_level => $Config->Get('LogToSyslog'),
- callbacks => [ $syslog_cb, $stack_cb ],
- stderr => 1,
- $Config->Get('LogToSyslogConf'),
- ));
+
+ unless ( -d $logdir && ( ( -f $filename && -w $filename ) || -w $logdir ) ) {
+ # localizing here would be hard when we don't have a current user yet
+ die "Log file $filename couldn't be written or created.\n RT can't run.";
}
+
+ package Log::Dispatch::File;
+ require Log::Dispatch::File;
+ $RT::Logger->add(Log::Dispatch::File->new
+ ( name=>'rtlog',
+ min_level=> $RT::LogToFile,
+ filename=> $filename,
+ mode=>'append',
+ callbacks => $simple_cb,
+ ));
+ }
+ if ($RT::LogToScreen) {
+ package Log::Dispatch::Screen;
+ require Log::Dispatch::Screen;
+ $RT::Logger->add(Log::Dispatch::Screen->new
+ ( name => 'screen',
+ min_level => $RT::LogToScreen,
+ callbacks => $simple_cb,
+ stderr => 1,
+ ));
+ }
+ if ($RT::LogToSyslog) {
+ package Log::Dispatch::Syslog;
+ require Log::Dispatch::Syslog;
+ $RT::Logger->add(Log::Dispatch::Syslog->new
+ ( name => 'syslog',
+ ident => 'RT',
+ min_level => $RT::LogToSyslog,
+ callbacks => $syslog_cb,
+ stderr => 1,
+ @RT::LogToSyslogConf
+ ));
+ }
+
}
+# {{{ Signal handlers
-# Signal handlers
## This is the default handling of warnings and die'ings in the code
## (including other used modules - maybe except for errors catched by
## Mason). It will log all problems through the standard logging
@@ -371,15 +355,16 @@ sub InitLogging {
#When we call die, trap it and log->crit with the value of the die.
- $SIG{__DIE__} = sub {
- # if we are not in eval and perl is not parsing code
- # then rollback transactions and log RT error
- unless ($^S || !defined $^S ) {
- $RT::Handle->Rollback(1) if $RT::Handle;
- $RT::Logger->crit("$_[0]") if $RT::Logger;
- }
- die $_[0];
- };
+$SIG{__DIE__} = sub {
+ unless ($^S || !defined $^S ) {
+ $RT::Handle->Rollback();
+ $RT::Logger->crit("$_[0]");
+ }
+ die $_[0];
+};
+
+# }}}
+
}
@@ -415,9 +400,10 @@ EOF
}
}
+
=head2 InitClasses
-Load all modules that define base classes.
+Load all modules that define base classes
=cut
@@ -440,8 +426,6 @@ sub InitClasses {
require RT::ObjectCustomFields;
require RT::ObjectCustomFieldValues;
require RT::Attributes;
- require RT::Dashboard;
- require RT::Approval;
# on a cold server (just after restart) people could have an object
# in the session, as we deserialize it so we never call constructor
@@ -468,196 +452,21 @@ sub InitClasses {
);
}
-=head2 InitSystemObjects
-
-Initializes system objects: C<$RT::System>, C<$RT::SystemUser>
-and C<$RT::Nobody>.
-
-=cut
-
-sub InitSystemObjects {
-
- #RT's system user is a genuine database user. its id lives here
- require RT::CurrentUser;
- $SystemUser = new RT::CurrentUser;
- $SystemUser->LoadByName('RT_System');
-
- #RT's "nobody user" is a genuine database user. its ID lives here.
- $Nobody = new RT::CurrentUser;
- $Nobody->LoadByName('Nobody');
-
- require RT::System;
- $System = RT::System->new( $SystemUser );
-}
-
-=head1 CLASS METHODS
-
-=head2 Config
-
-Returns the current L<config object RT::Config>, but note that
-you must L<load config /LoadConfig> first otherwise this method
-returns undef.
-
-Method can be called as class method.
-
-=cut
-
-sub Config { return $Config }
-
-=head2 DatabaseHandle
-
-Returns the current L<database handle object RT::Handle>.
-
-See also L</ConnectToDatabase>.
-
-=cut
-
-sub DatabaseHandle { return $Handle }
-
-=head2 Logger
-
-Returns the logger. See also L</InitLogging>.
-
-=cut
-
-sub Logger { return $Logger }
-
-=head2 System
-
-Returns the current L<system object RT::System>. See also
-L</InitSystemObjects>.
-
-=cut
-
-sub System { return $System }
-
-=head2 SystemUser
-
-Returns the system user's object, it's object of
-L<RT::CurrentUser> class that represents the system. See also
-L</InitSystemObjects>.
-
-=cut
-
-sub SystemUser { return $SystemUser }
-
-=head2 Nobody
-
-Returns object of Nobody. It's object of L<RT::CurrentUser> class
-that represents a user who can own ticket and nothing else. See
-also L</InitSystemObjects>.
-
-=cut
-
-sub Nobody { return $Nobody }
-
-=head2 Plugins
-
-Returns a listref of all Plugins currently configured for this RT instance.
-You can define plugins by adding them to the @Plugins list in your RT_SiteConfig
-
-=cut
-
-our @PLUGINS = ();
-sub Plugins {
- my $self = shift;
- unless (@PLUGINS) {
- $self->InitPluginPaths;
- @PLUGINS = $self->InitPlugins;
- }
- return \@PLUGINS;
-}
-
-=head2 PluginDirs
-
-Takes optional subdir (e.g. po, lib, etc.) and return plugins' dirs that exist.
-
-This code chacke plugins names or anything else and required when main config
-is loaded to load plugins' configs.
-
-=cut
-
-sub PluginDirs {
- my $self = shift;
- my $subdir = shift;
-
- require RT::Plugin;
-
- my @res;
- foreach my $plugin (grep $_, RT->Config->Get('Plugins')) {
- my $path = RT::Plugin->new( name => $plugin )->Path( $subdir );
- next unless -d $path;
- push @res, $path;
- }
- return @res;
-}
-
-=head2 InitPluginPaths
-
-Push plugins' lib paths into @INC right after F<local/lib>.
-In case F<local/lib> isn't in @INC, append them to @INC
-
-=cut
-
-sub InitPluginPaths {
- my $self = shift || __PACKAGE__;
-
- my @lib_dirs = $self->PluginDirs('lib');
-
- my @tmp_inc;
- my $added;
- for (@INC) {
- if ( Cwd::realpath($_) eq $RT::LocalLibPath) {
- push @tmp_inc, $_, @lib_dirs;
- $added = 1;
- } else {
- push @tmp_inc, $_;
- }
- }
-
- # append @lib_dirs in case $RT::LocalLibPath isn't in @INC
- push @tmp_inc, @lib_dirs unless $added;
-
- my %seen;
- @INC = grep !$seen{$_}++, @tmp_inc;
-}
-
-=head2 InitPlugins
-
-Initialze all Plugins found in the RT configuration file, setting up their lib and HTML::Mason component roots.
+# }}}
-=cut
-
-sub InitPlugins {
- my $self = shift;
- my @plugins;
- require RT::Plugin;
- foreach my $plugin (grep $_, RT->Config->Get('Plugins')) {
- $plugin->require;
- die $UNIVERSAL::require::ERROR if ($UNIVERSAL::require::ERROR);
- push @plugins, RT::Plugin->new(name =>$plugin);
- }
- return @plugins;
-}
+sub SystemUser {
+ return($SystemUser);
+}
-sub InstallMode {
- my $self = shift;
- if (@_) {
- $_INSTALL_MODE = shift;
- if($_INSTALL_MODE) {
- require RT::CurrentUser;
- $SystemUser = RT::CurrentUser->new();
- }
- }
- return $_INSTALL_MODE;
+sub Nobody {
+ return ($Nobody);
}
-
=head1 BUGS
-Please report them to rt-bugs@bestpractical.com, if you know what's
-broken and have at least some idea of what needs to be fixed.
+Please report them to rt-bugs@fsck.com, if you know what's broken and have at least
+some idea of what needs to be fixed.
If you're not sure what's going on, report them rt-devel@lists.bestpractical.com.
@@ -666,6 +475,14 @@ If you're not sure what's going on, report them rt-devel@lists.bestpractical.com
L<RT::StyleGuide>
L<DBIx::SearchBuilder>
+=begin testing
+
+ok ($RT::Nobody->Name() eq 'Nobody', "Nobody is nobody");
+ok ($RT::Nobody->Name() ne 'root', "Nobody isn't named root");
+ok ($RT::SystemUser->Name() eq 'RT_System', "The system user is RT_System");
+ok ($RT::SystemUser->Name() ne 'noname', "The system user isn't noname");
+
+=end testing
=cut
diff --git a/rt/lib/RT/ACE.pm b/rt/lib/RT/ACE.pm
index 7f21ba05e..1501a125e 100755
--- a/rt/lib/RT/ACE.pm
+++ b/rt/lib/RT/ACE.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
+# 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.
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -69,7 +44,11 @@ RT::ACE
=cut
package RT::ACE;
-use base 'RT::Record';
+use RT::Record;
+
+
+use vars qw( @ISA );
+@ISA= qw( RT::Record );
sub _Init {
my $self = shift;
@@ -82,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -125,7 +104,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -134,14 +113,14 @@ Returns the current value of id.
=cut
-=head2 PrincipalType
+=item PrincipalType
Returns the current value of PrincipalType.
(In the database, PrincipalType is stored as varchar(25).)
-=head2 SetPrincipalType VALUE
+=item SetPrincipalType VALUE
Set PrincipalType to VALUE.
@@ -152,14 +131,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 PrincipalId
+=item PrincipalId
Returns the current value of PrincipalId.
(In the database, PrincipalId is stored as int(11).)
-=head2 SetPrincipalId VALUE
+=item SetPrincipalId VALUE
Set PrincipalId to VALUE.
@@ -170,14 +149,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 RightName
+=item RightName
Returns the current value of RightName.
(In the database, RightName is stored as varchar(25).)
-=head2 SetRightName VALUE
+=item SetRightName VALUE
Set RightName to VALUE.
@@ -188,14 +167,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ObjectType
+=item ObjectType
Returns the current value of ObjectType.
(In the database, ObjectType is stored as varchar(25).)
-=head2 SetObjectType VALUE
+=item SetObjectType VALUE
Set ObjectType to VALUE.
@@ -206,14 +185,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ObjectId
+=item ObjectId
Returns the current value of ObjectId.
(In the database, ObjectId is stored as int(11).)
-=head2 SetObjectId VALUE
+=item SetObjectId VALUE
Set ObjectId to VALUE.
@@ -224,14 +203,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 DelegatedBy
+=item DelegatedBy
Returns the current value of DelegatedBy.
(In the database, DelegatedBy is stored as int(11).)
-=head2 SetDelegatedBy VALUE
+=item SetDelegatedBy VALUE
Set DelegatedBy to VALUE.
@@ -242,14 +221,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 DelegatedFrom
+=item DelegatedFrom
Returns the current value of DelegatedFrom.
(In the database, DelegatedFrom is stored as int(11).)
-=head2 SetDelegatedFrom VALUE
+=item SetDelegatedFrom VALUE
Set DelegatedFrom to VALUE.
@@ -261,25 +240,25 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
PrincipalType =>
- {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
+ {read => 1, write => 1, type => 'varchar(25)', default => ''},
PrincipalId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
RightName =>
- {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
+ {read => 1, write => 1, type => 'varchar(25)', default => ''},
ObjectType =>
- {read => 1, write => 1, sql_type => 12, length => 25, is_blob => 0, is_numeric => 0, type => 'varchar(25)', default => ''},
+ {read => 1, write => 1, type => 'varchar(25)', default => ''},
ObjectId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
DelegatedBy =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
DelegatedFrom =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
}
};
@@ -311,7 +290,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/ACL.pm b/rt/lib/RT/ACL.pm
index 1dc66e8b6..81f59c6d0 100755
--- a/rt/lib/RT/ACL.pm
+++ b/rt/lib/RT/ACL.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -72,9 +47,12 @@ use strict;
package RT::ACL;
-use base 'RT::SearchBuilder';
+use RT::SearchBuilder;
use RT::ACE;
+use vars qw( @ISA );
+@ISA= qw(RT::SearchBuilder);
+
sub _Init {
my $self = shift;
@@ -86,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::ACE item
@@ -123,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Action/Autoreply.pm b/rt/lib/RT/Action/Autoreply.pm
index 3734d819a..81f7bddfa 100755
--- a/rt/lib/RT/Action/Autoreply.pm
+++ b/rt/lib/RT/Action/Autoreply.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,51 +14,20 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
package RT::Action::Autoreply;
+require RT::Action::SendEmail;
use strict;
-use warnings;
-
-use base qw(RT::Action::SendEmail);
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::SendEmail);
-=head2 Prepare
-
-Set up the relevant recipients, then call our parent.
-
-=cut
-
-
-sub Prepare {
- my $self = shift;
- $self->SetRecipients();
- $self->SUPER::Prepare();
-}
# {{{ sub SetRecipients
@@ -96,37 +59,35 @@ Set this message\'s return address to the apropriate queue address
sub SetReturnAddress {
my $self = shift;
+ my %args = ( is_comment => 0,
+ @_
+ );
- my $friendly_name;
-
- if (RT->Config->Get('UseFriendlyFromLine')) {
- $friendly_name = $self->TicketObj->QueueObj->Description ||
- $self->TicketObj->QueueObj->Name;
- }
-
- $self->SUPER::SetReturnAddress( @_, friendly_name => $friendly_name );
+ my $replyto;
+ if ($args{'is_comment'}) {
+ $replyto = $self->TicketObj->QueueObj->CommentAddress ||
+ $RT::CommentAddress;
+ }
+ else {
+ $replyto = $self->TicketObj->QueueObj->CorrespondAddress ||
+ $RT::CorrespondAddress;
+ }
+
+ unless ($self->TemplateObj->MIMEObj->head->get('From')) {
+ my $friendly_name = $self->TicketObj->QueueObj->Description ||
+ $self->TicketObj->QueueObj->Name;
+ $friendly_name =~ s/"/\\"/g;
+ $self->SetHeader('From', "\"$friendly_name\" <$replyto>");
+ }
+
+ unless ($self->TemplateObj->MIMEObj->head->get('Reply-To')) {
+ $self->SetHeader('Reply-To', "$replyto");
+ }
}
# }}}
-# {{{{ sub SetRTSpecialHeaders
-
-=head2 SetRTSpecialHeaders
-
-Set the C<Auto-Generated> header to C<auto-replied>, in accordance
-with RFC3834.
-
-=cut
-
-sub SetRTSpecialHeaders {
- my $self = shift;
- $self->SUPER::SetRTSpecialHeaders(@_);
- $self->SetHeader( 'Auto-Submitted', 'auto-replied' );
-}
-
-# }}}
-
eval "require RT::Action::Autoreply_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Autoreply_Vendor.pm});
eval "require RT::Action::Autoreply_Local";
diff --git a/rt/lib/RT/Action/Generic.pm b/rt/lib/RT/Action/Generic.pm
index 5e8ef32ce..007d299c7 100755
--- a/rt/lib/RT/Action/Generic.pm
+++ b/rt/lib/RT/Action/Generic.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,35 +14,16 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
+# 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.
#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
=head1 NAME
- RT::Action::Generic - deprecated, see RT::Action
+ RT::Action::Generic - a generic baseclass for RT Actions
=head1 SYNOPSIS
@@ -56,25 +31,165 @@
=head1 DESCRIPTION
-This module is provided only for backwards compatibility.
-
=head1 METHODS
+=begin testing
+
+ok (require RT::Action::Generic);
+
+=end testing
=cut
-use strict;
-use warnings;
package RT::Action::Generic;
-use base 'RT::Action';
+
+use strict;
+
+# {{{ sub new
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->_Init(@_);
+ return $self;
+}
+# }}}
+
+# {{{ sub new
+sub loc {
+ my $self = shift;
+ return $self->{'ScripObj'}->loc(@_);
+}
+# }}}
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ my %args = ( TransactionObj => undef,
+ TicketObj => undef,
+ ScripObj => undef,
+ TemplateObj => undef,
+ Argument => undef,
+ Type => undef,
+ @_ );
+
+
+ $self->{'Argument'} = $args{'Argument'};
+ $self->{'ScripObj'} = $args{'ScripObj'};
+ $self->{'TicketObj'} = $args{'TicketObj'};
+ $self->{'TransactionObj'} = $args{'TransactionObj'};
+ $self->{'TemplateObj'} = $args{'TemplateObj'};
+ $self->{'Type'} = $args{'Type'};
+}
+# }}}
+
+# Access Scripwide data
+
+# {{{ sub Argument
+sub Argument {
+ my $self = shift;
+ return($self->{'Argument'});
+}
+# }}}
+
+# {{{ sub TicketObj
+sub TicketObj {
+ my $self = shift;
+ return($self->{'TicketObj'});
+}
+# }}}
+
+# {{{ sub TransactionObj
+sub TransactionObj {
+ my $self = shift;
+ return($self->{'TransactionObj'});
+}
+# }}}
+
+# {{{ sub TemplateObj
+sub TemplateObj {
+ my $self = shift;
+ return($self->{'TemplateObj'});
+}
+# }}}
+
+# {{{ sub ScripObj
+sub ScripObj {
+ my $self = shift;
+ return($self->{'ScripObj'});
+}
+# }}}
+
+# {{{ sub Type
+sub Type {
+ my $self = shift;
+ return($self->{'Type'});
+}
+# }}}
+
+
+# Scrip methods
+
+#Do what we need to do and send it out.
+
+# {{{ sub Commit
+sub Commit {
+ my $self = shift;
+ return(0, $self->loc("Commit Stubbed"));
+}
+# }}}
+
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return $self->loc("No description for [_1]", ref $self);
+}
+# }}}
+
+
+#Parse the templates, get things ready to go.
+
+# {{{ sub Prepare
+sub Prepare {
+ my $self = shift;
+ return (0, $self->loc("Prepare Stubbed"));
+}
+# }}}
+
+
+#If this rule applies to this transaction, return true.
+
+# {{{ sub IsApplicable
+sub IsApplicable {
+ my $self = shift;
+ return(undef);
+}
+# }}}
+
+# {{{ sub DESTROY
+sub DESTROY {
+ my $self = shift;
+
+ # We need to clean up all the references that might maybe get
+ # oddly circular
+ $self->{'TemplateObj'} =undef
+ $self->{'TicketObj'} = undef;
+ $self->{'TransactionObj'} = undef;
+ $self->{'ScripObj'} = undef;
+
+
+
+}
+
+# }}}
eval "require RT::Action::Generic_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Generic_Vendor.pm});
-warn "RT::Action::Generic has become RT::Action. Please adjust your deprecated RT::Action::Generic_Vendor file at " . $INC{"RT/Action/Generic_Vendor.pm"} if !$@;
-
eval "require RT::Action::Generic_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Generic_Local.pm});
-warn "RT::Action::Generic has become RT::Action. Please adjust your deprecated RT::Action::Generic_Local file at " . $INC{"RT/Action/Generic_Local.pm"} if !$@;
1;
-
diff --git a/rt/lib/RT/Action/Notify.pm b/rt/lib/RT/Action/Notify.pm
index 30238fd61..1e4e4c073 100755
--- a/rt/lib/RT/Action/Notify.pm
+++ b/rt/lib/RT/Action/Notify.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,54 +14,21 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
+# 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.
#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
#
-# END BPS TAGGED BLOCK }}}
-
-#
+# END LICENSE BLOCK
package RT::Action::Notify;
+require RT::Action::SendEmail;
use strict;
-use warnings;
-
-use base qw(RT::Action::SendEmail);
-
-use Email::Address;
-
-=head2 Prepare
-
-Set up the relevant recipients, then call our parent.
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::SendEmail);
-=cut
-
-
-sub Prepare {
- my $self = shift;
- $self->SetRecipients();
- $self->SUPER::Prepare();
-}
+# {{{ sub SetRecipients
=head2 SetRecipients
@@ -79,63 +40,65 @@ Explicitly B<does not> notify the creator of the transaction by default
sub SetRecipients {
my $self = shift;
- my $ticket = $self->TicketObj;
-
my $arg = $self->Argument;
+
$arg =~ s/\bAll\b/Owner,Requestor,AdminCc,Cc/;
my ( @To, @PseudoTo, @Cc, @Bcc );
- if ( $arg =~ /\bOtherRecipients\b/ ) {
- if ( my $attachment = $self->TransactionObj->Attachments->First ) {
- push @Cc, map { $_->address } Email::Address->parse(
- $attachment->GetHeader('RT-Send-Cc')
- );
- push @Bcc, map { $_->address } Email::Address->parse(
- $attachment->GetHeader('RT-Send-Bcc')
- );
+ if ($arg =~ /\bOtherRecipients\b/) {
+ if ($self->TransactionObj->Attachments->First) {
+ push (@Cc, $self->TransactionObj->Attachments->First->GetHeader('RT-Send-Cc'));
+ push (@Bcc, $self->TransactionObj->Attachments->First->GetHeader('RT-Send-Bcc'));
}
}
if ( $arg =~ /\bRequestor\b/ ) {
- push @To, $ticket->Requestors->MemberEmailAddresses;
+ push ( @To, $self->TicketObj->Requestors->MemberEmailAddresses );
}
+
+
if ( $arg =~ /\bCc\b/ ) {
#If we have a To, make the Ccs, Ccs, otherwise, promote them to To
if (@To) {
- push ( @Cc, $ticket->Cc->MemberEmailAddresses );
- push ( @Cc, $ticket->QueueObj->Cc->MemberEmailAddresses );
+ push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses );
+ push ( @Cc, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses );
}
else {
- push ( @Cc, $ticket->Cc->MemberEmailAddresses );
- push ( @To, $ticket->QueueObj->Cc->MemberEmailAddresses );
+ push ( @Cc, $self->TicketObj->Cc->MemberEmailAddresses );
+ push ( @To, $self->TicketObj->QueueObj->Cc->MemberEmailAddresses );
}
}
- if ( $arg =~ /\bOwner\b/ && $ticket->OwnerObj->id != $RT::Nobody->id ) {
- # If we're not sending to Ccs or requestors,
+ if ( ( $arg =~ /\bOwner\b/ )
+ && ( $self->TicketObj->OwnerObj->id != $RT::Nobody->id ) )
+ {
+
+ # If we're not sending to Ccs or requestors,
# then the Owner can be the To.
if (@To) {
- push ( @Bcc, $ticket->OwnerObj->EmailAddress );
+ push ( @Bcc, $self->TicketObj->OwnerObj->EmailAddress );
}
else {
- push ( @To, $ticket->OwnerObj->EmailAddress );
+ push ( @To, $self->TicketObj->OwnerObj->EmailAddress );
}
}
if ( $arg =~ /\bAdminCc\b/ ) {
- push ( @Bcc, $ticket->AdminCc->MemberEmailAddresses );
- push ( @Bcc, $ticket->QueueObj->AdminCc->MemberEmailAddresses );
+ push ( @Bcc, $self->TicketObj->AdminCc->MemberEmailAddresses );
+ push ( @Bcc, $self->TicketObj->QueueObj->AdminCc->MemberEmailAddresses );
}
- if ( RT->Config->Get('UseFriendlyToLine') ) {
+ if ($RT::UseFriendlyToLine) {
unless (@To) {
- push @PseudoTo,
- sprintf RT->Config->Get('FriendlyToLineFormat'), $arg, $ticket->id;
+ push (
+ @PseudoTo,
+ sprintf($RT::FriendlyToLineFormat, $arg, $self->TicketObj->id),
+ );
}
}
@@ -144,21 +107,23 @@ sub SetRecipients {
#Strip the sender out of the To, Cc and AdminCc and set the
# recipients fields used to build the message by the superclass.
# unless a flag is set
- if (RT->Config->Get('NotifyActor')) {
+ if ($RT::NotifyActor) {
@{ $self->{'To'} } = @To;
@{ $self->{'Cc'} } = @Cc;
@{ $self->{'Bcc'} } = @Bcc;
}
else {
- @{ $self->{'To'} } = grep ( lc $_ ne lc $creator, @To );
- @{ $self->{'Cc'} } = grep ( lc $_ ne lc $creator, @Cc );
- @{ $self->{'Bcc'} } = grep ( lc $_ ne lc $creator, @Bcc );
+ @{ $self->{'To'} } = grep ( !/^$creator$/, @To );
+ @{ $self->{'Cc'} } = grep ( !/^$creator$/, @Cc );
+ @{ $self->{'Bcc'} } = grep ( !/^$creator$/, @Bcc );
}
@{ $self->{'PseudoTo'} } = @PseudoTo;
-
+ return (1);
}
+# }}}
+
eval "require RT::Action::Notify_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/Notify_Vendor.pm});
eval "require RT::Action::Notify_Local";
diff --git a/rt/lib/RT/Action/NotifyAsComment.pm b/rt/lib/RT/Action/NotifyAsComment.pm
index b2eb5acd8..210e4ab15 100755
--- a/rt/lib/RT/Action/NotifyAsComment.pm
+++ b/rt/lib/RT/Action/NotifyAsComment.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,38 +14,20 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
package RT::Action::NotifyAsComment;
require RT::Action::Notify;
use strict;
-use warnings;
-use base qw(RT::Action::Notify);
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Notify);
+
=head2 SetReturnAddress
diff --git a/rt/lib/RT/Action/ResolveMembers.pm b/rt/lib/RT/Action/ResolveMembers.pm
index ff826ccc1..02ff3a58c 100644
--- a/rt/lib/RT/Action/ResolveMembers.pm
+++ b/rt/lib/RT/Action/ResolveMembers.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,39 +14,22 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# This Action will resolve all members of a resolved group ticket
package RT::Action::ResolveMembers;
-use base 'RT::Action';
+require RT::Action::Generic;
require RT::Links;
use strict;
+use vars qw/@ISA/;
+@ISA=qw(RT::Action::Generic);
#Do what we need to do and send it out.
diff --git a/rt/lib/RT/Action/SendEmail.pm b/rt/lib/RT/Action/SendEmail.pm
index a09bd3e56..dac8fc8e7 100755
--- a/rt/lib/RT/Action/SendEmail.pm
+++ b/rt/lib/RT/Action/SendEmail.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,46 +14,25 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
+# 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.
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Portions Copyright 2000 Tobias Brox <tobix@cpan.org>
package RT::Action::SendEmail;
+require RT::Action::Generic;
use strict;
-use warnings;
+use vars qw/@ISA/;
+@ISA = qw(RT::Action::Generic);
-use base qw(RT::Action);
+use MIME::Words qw(encode_mimeword);
use RT::EmailParser;
-use RT::Interface::Email;
-use Email::Address;
-our @EMAIL_RECIPIENT_HEADERS = qw(To Cc Bcc);
-
=head1 NAME
@@ -69,493 +42,273 @@ RT::Action::AutoReply is a good example subclass.
=head1 SYNOPSIS
- use base 'RT::Action::SendEmail';
+ require RT::Action::SendEmail;
+ @ISA = qw(RT::Action::SendEmail);
+
=head1 DESCRIPTION
Basically, you create another module RT::Action::YourAction which ISA
RT::Action::SendEmail.
-=head1 METHODS
+If you want to set the recipients of the mail to something other than
+the addresses mentioned in the To, Cc, Bcc and headers in
+the template, you should subclass RT::Action::SendEmail and override
+either the SetRecipients method or the SetTo, SetCc, etc methods (see
+the comments for the SetRecipients sub).
-=head2 CleanSlate
-Cleans class-wide options, like L</SquelchMailTo> or L</AttachTickets>.
+=begin testing
-=cut
+ok (require RT::Action::SendEmail);
-sub CleanSlate {
- my $self = shift;
- $self->SquelchMailTo(undef);
- $self->AttachTickets(undef);
-}
+=end testing
-=head2 Commit
-Sends the prepared message and writes outgoing record into DB if the feature is
-activated in the config.
+=head1 AUTHOR
-=cut
+Jesse Vincent <jesse@bestpractical.com> and Tobias Brox <tobix@cpan.org>
-sub Commit {
- my $self = shift;
+=head1 SEE ALSO
- $self->DeferDigestRecipients() if RT->Config->Get('RecordOutgoingEmail');
- my $message = $self->TemplateObj->MIMEObj;
-
- my $orig_message;
- if ( RT->Config->Get('RecordOutgoingEmail')
- && RT->Config->Get('GnuPG')->{'Enable'} )
- {
-
- # it's hacky, but we should know if we're going to crypt things
- my $attachment = $self->TransactionObj->Attachments->First;
-
- my %crypt;
- foreach my $argument (qw(Sign Encrypt)) {
- if ( $attachment
- && defined $attachment->GetHeader("X-RT-$argument") )
- {
- $crypt{$argument} = $attachment->GetHeader("X-RT-$argument");
- } else {
- $crypt{$argument} = $self->TicketObj->QueueObj->$argument();
- }
- }
- if ( $crypt{'Sign'} || $crypt{'Encrypt'} ) {
- $orig_message = $message->dup;
- }
- }
-
- my ($ret) = $self->SendMessage($message);
- if ( $ret > 0 && RT->Config->Get('RecordOutgoingEmail') ) {
- if ($orig_message) {
- $message->attach(
- Type => 'application/x-rt-original-message',
- Disposition => 'inline',
- Data => $orig_message->as_string,
- );
- }
- $self->RecordOutgoingMailTransaction($message);
- $self->RecordDeferredRecipients();
- }
+perl(1).
+=cut
- return ( abs $ret );
-}
+# {{{ Scrip methods (_Init, Commit, Prepare, IsApplicable)
-=head2 Prepare
+# {{{ sub _Init
+# We use _Init from RT::Action
+# }}}
-Builds an outgoing email we're going to send using scrip's template.
+# {{{ sub Commit
+#Do what we need to do and send it out.
+sub Commit {
+ my $self = shift;
-=cut
+ my $MIMEObj = $self->TemplateObj->MIMEObj;
+ my $msgid = $MIMEObj->head->get('Message-Id');
+ chomp $msgid;
+ $RT::Logger->info($msgid." #".$self->TicketObj->id."/".$self->TransactionObj->id." - Scrip ". $self->ScripObj->id ." ".$self->ScripObj->Description);
+ #send the email
-sub Prepare {
- my $self = shift;
+ # Weed out any RT addresses. We really don't want to talk to ourselves!
+ @{$self->{'To'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'To'}});
+ @{$self->{'Cc'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'Cc'}});
+ @{$self->{'Bcc'}} = RT::EmailParser::CullRTAddresses("", @{$self->{'Bcc'}});
+ # If there are no recipients, don't try to send the message.
+ # If the transaction has content and has the header RT-Squelch-Replies-To
- my ( $result, $message ) = $self->TemplateObj->Parse(
- Argument => $self->Argument,
- TicketObj => $self->TicketObj,
- TransactionObj => $self->TransactionObj
- );
- if ( !$result ) {
- return (undef);
- }
+ if ( defined $self->TransactionObj->Attachments->First() ) {
- my $MIMEObj = $self->TemplateObj->MIMEObj;
+ my $squelch = $self->TransactionObj->Attachments->First->GetHeader( 'RT-Squelch-Replies-To');
- # Header
- $self->SetRTSpecialHeaders();
+ if ($squelch) {
+ my @blacklist = split ( /,/, $squelch );
- $self->RemoveInappropriateRecipients();
+ # Cycle through the people we're sending to and pull out anyone on the
+ # system blacklist
- my %seen;
- foreach my $type (@EMAIL_RECIPIENT_HEADERS) {
- @{ $self->{$type} }
- = grep defined && length && !$seen{ lc $_ }++,
- @{ $self->{$type} };
+ foreach my $person_to_yank (@blacklist) {
+ $person_to_yank =~ s/\s//g;
+ @{ $self->{'To'} } =
+ grep ( !/^$person_to_yank$/, @{ $self->{'To'} } );
+ @{ $self->{'Cc'} } =
+ grep ( !/^$person_to_yank$/, @{ $self->{'Cc'} } );
+ @{ $self->{'Bcc'} } =
+ grep ( !/^$person_to_yank$/, @{ $self->{'Bcc'} } );
+ }
+ }
}
# Go add all the Tos, Ccs and Bccs that we need to to the message to
# make it happy, but only if we actually have values in those arrays.
-# TODO: We should be pulling the recipients out of the template and shove them into To, Cc and Bcc
+ $self->SetHeader( 'To', join ( ',', @{ $self->{'To'} } ) )
+ if ( $self->{'To'} && @{ $self->{'To'} } );
+ $self->SetHeader( 'Cc', join ( ',', @{ $self->{'Cc'} } ) )
+ if ( $self->{'Cc'} && @{ $self->{'Cc'} } );
+ $self->SetHeader( 'Bcc', join ( ',', @{ $self->{'Bcc'} } ) )
+ if ( $self->{'Cc'} && @{ $self->{'Bcc'} } );
- for my $header (@EMAIL_RECIPIENT_HEADERS) {
- $self->SetHeader( $header, join( ', ', @{ $self->{$header} } ) )
- if ( !$MIMEObj->head->get($header)
- && $self->{$header}
- && @{ $self->{$header} } );
-}
- # PseudoTo (fake to headers) shouldn't get matched for message recipients.
- # If we don't have any 'To' header (but do have other recipients), drop in
- # the pseudo-to header.
- $self->SetHeader( 'To', join( ', ', @{ $self->{'PseudoTo'} } ) )
- if $self->{'PseudoTo'}
- && @{ $self->{'PseudoTo'} }
- && !$MIMEObj->head->get('To')
- && ( $MIMEObj->head->get('Cc') or $MIMEObj->head->get('Bcc') );
-
- # We should never have to set the MIME-Version header
- $self->SetHeader( 'MIME-Version', '1.0' );
-
- # fsck.com #5959: Since RT sends 8bit mail, we should say so.
- $self->SetHeader( 'Content-Transfer-Encoding', '8bit' );
-
- # For security reasons, we only send out textual mails.
- foreach my $part ( grep !$_->is_multipart, $MIMEObj->parts_DFS ) {
- my $type = $part->mime_type || 'text/plain';
- $type = 'text/plain' unless RT::I18N::IsTextualContentType($type);
- $part->head->mime_attr( "Content-Type" => $type );
- $part->head->mime_attr( "Content-Type.charset" => 'utf-8' );
- }
+ $self->SetHeader('MIME-Version', '1.0');
- RT::I18N::SetMIMEEntityToEncoding( $MIMEObj,
- RT->Config->Get('EmailOutputEncoding'),
- 'mime_words_ok', );
+ # try to convert message body from utf-8 to $RT::EmailOutputEncoding
+ $self->SetHeader( 'Content-Type', 'text/plain; charset="utf-8"' );
- # Build up a MIME::Entity that looks like the original message.
- $self->AddAttachments if ( $MIMEObj->head->get('RT-Attach-Message')
- && ( $MIMEObj->head->get('RT-Attach-Message') !~ /^(n|no|0|off|false)$/i ) );
-
- $self->AddTickets;
-
- my $attachment = $self->TransactionObj->Attachments->First;
- if ($attachment
- && !(
- $attachment->GetHeader('X-RT-Encrypt')
- || $self->TicketObj->QueueObj->Encrypt
- )
- )
- {
- $attachment->SetHeader( 'X-RT-Encrypt' => 1 )
- if ( $attachment->GetHeader("X-RT-Incoming-Encryption") || '' ) eq
- 'Success';
- }
+ RT::I18N::SetMIMEEntityToEncoding( $MIMEObj, $RT::EmailOutputEncoding, 'mime_words_ok' );
+ $self->SetHeader( 'Content-Type', 'text/plain; charset="' . $RT::EmailOutputEncoding . '"' );
- return $result;
-}
-=head2 To
+ # Build up a MIME::Entity that looks like the original message.
-Returns an array of L<Email::Address> objects containing all the To: recipients for this notification
+ my $do_attach = $self->TemplateObj->MIMEObj->head->get('RT-Attach-Message');
-=cut
+ if ($do_attach) {
+ $self->TemplateObj->MIMEObj->head->delete('RT-Attach-Message');
-sub To {
- my $self = shift;
- return ( $self->AddressesFromHeader('To') );
-}
+ my $attachments = RT::Attachments->new($RT::SystemUser);
+ $attachments->Limit( FIELD => 'TransactionId',
+ VALUE => $self->TransactionObj->Id );
+ $attachments->OrderBy('id');
-=head2 Cc
+ my $transaction_content_obj = $self->TransactionObj->ContentObj;
-Returns an array of L<Email::Address> objects containing all the Cc: recipients for this notification
+ # attach any of this transaction's attachments
+ while ( my $attach = $attachments->Next ) {
-=cut
+ # Don't attach anything blank
+ next unless ( $attach->ContentLength );
-sub Cc {
- my $self = shift;
- return ( $self->AddressesFromHeader('Cc') );
-}
-
-=head2 Bcc
-
-Returns an array of L<Email::Address> objects containing all the Bcc: recipients for this notification
+ # We want to make sure that we don't include the attachment that's being sued as the "Content" of this message"
+ next
+ if ( $transaction_content_obj
+ && $transaction_content_obj->Id == $attach->Id
+ && $transaction_content_obj->ContentType =~ qr{text/plain}i
+ );
+ $MIMEObj->make_multipart('mixed');
+ $MIMEObj->attach( Type => $attach->ContentType,
+ Charset => $attach->OriginalEncoding,
+ Data => $attach->OriginalContent,
+ Filename => $self->MIMEEncodeString( $attach->Filename, $RT::EmailOutputEncoding ),
+ Encoding => '-SUGGEST');
+ }
-=cut
+ }
-sub Bcc {
- my $self = shift;
- return ( $self->AddressesFromHeader('Bcc') );
-}
+ my $retval = $self->SendMessage($MIMEObj);
-sub AddressesFromHeader {
- my $self = shift;
- my $field = shift;
- my $header = $self->TemplateObj->MIMEObj->head->get($field);
- my @addresses = Email::Address->parse($header);
- return (@addresses);
+ return ($retval);
}
-=head2 SendMessage MIMEObj
-
-sends the message using RT's preferred API.
-TODO: Break this out to a separate module
-
-=cut
-
-sub SendMessage {
-
- # DO NOT SHIFT @_ in this subroutine. It breaks Hook::LexWrap's
- # ability to pass @_ to a 'post' routine.
- my ( $self, $MIMEObj ) = @_;
-
- my $msgid = $MIMEObj->head->get('Message-ID');
- chomp $msgid;
+# }}}
- $self->ScripActionObj->{_Message_ID}++;
+# {{{ sub Prepare
- $RT::Logger->info( $msgid . " #"
- . $self->TicketObj->id . "/"
- . $self->TransactionObj->id
- . " - Scrip "
- . ($self->ScripObj->id || '#rule'). " "
- . ( $self->ScripObj->Description || '' ) );
-
- my $status = RT::Interface::Email::SendEmail(
- Entity => $MIMEObj,
- Ticket => $self->TicketObj,
- Transaction => $self->TransactionObj,
- );
-
-
- return $status unless ($status > 0 || exists $self->{'Deferred'});
+sub Prepare {
+ my $self = shift;
- my $success = $msgid . " sent ";
- foreach (@EMAIL_RECIPIENT_HEADERS) {
- my $recipients = $MIMEObj->head->get($_);
- $success .= " $_: " . $recipients if $recipients;
- }
+ # This actually populates the MIME::Entity fields in the Template Object
- if( exists $self->{'Deferred'} ) {
- for (qw(daily weekly susp)) {
- $success .= "\nBatched email $_ for: ". join(", ", keys %{ $self->{'Deferred'}{ $_ } } )
- if exists $self->{'Deferred'}{ $_ };
- }
+ unless ( $self->TemplateObj ) {
+ $RT::Logger->warning("No template object handed to $self\n");
}
- $success =~ s/\n//g;
-
- $RT::Logger->info($success);
-
- return (1);
-}
+ unless ( $self->TransactionObj ) {
+ $RT::Logger->warning("No transaction object handed to $self\n");
-=head2 AddAttachments
-
-Takes any attachments to this transaction and attaches them to the message
-we're building.
-
-=cut
-
-sub AddAttachments {
- my $self = shift;
-
- my $MIMEObj = $self->TemplateObj->MIMEObj;
-
- $MIMEObj->head->delete('RT-Attach-Message');
+ }
- my $attachments = RT::Attachments->new($RT::SystemUser);
- $attachments->Limit(
- FIELD => 'TransactionId',
- VALUE => $self->TransactionObj->Id
- );
+ unless ( $self->TicketObj ) {
+ $RT::Logger->warning("No ticket object handed to $self\n");
- # Don't attach anything blank
- $attachments->LimitNotEmpty;
- $attachments->OrderBy( FIELD => 'id' );
-
- # We want to make sure that we don't include the attachment that's
- # being used as the "Content" of this message" unless that attachment's
- # content type is not like text/...
- my $transaction_content_obj = $self->TransactionObj->ContentObj;
-
- if ( $transaction_content_obj
- && $transaction_content_obj->ContentType =~ m{text/}i )
- {
- # If this was part of a multipart/alternative, skip all of the kids
- my $parent = $transaction_content_obj->ParentObj;
- if ($parent and $parent->Id and $parent->ContentType eq "multipart/alternative") {
- $attachments->Limit(
- ENTRYAGGREGATOR => 'AND',
- FIELD => 'parent',
- OPERATOR => '!=',
- VALUE => $parent->Id,
- );
- } else {
- $attachments->Limit(
- ENTRYAGGREGATOR => 'AND',
- FIELD => 'id',
- OPERATOR => '!=',
- VALUE => $transaction_content_obj->Id,
- );
- }
}
- # attach any of this transaction's attachments
- my $seen_attachment = 0;
- while ( my $attach = $attachments->Next ) {
- if ( !$seen_attachment ) {
- $MIMEObj->make_multipart( 'mixed', Force => 1 );
- $seen_attachment = 1;
+ my ( $result, $message ) = $self->TemplateObj->Parse(
+ Argument => $self->Argument,
+ TicketObj => $self->TicketObj,
+ TransactionObj => $self->TransactionObj
+ );
+ if ($result) {
+
+ # Header
+ $self->SetSubject();
+ $self->SetSubjectToken();
+ $self->SetRecipients();
+ $self->SetReturnAddress();
+ $self->SetRTSpecialHeaders();
+ if ($RT::EmailOutputEncoding) {
+
+ # l10n related header
+ $self->SetHeaderAsEncoding( 'Subject', $RT::EmailOutputEncoding );
}
- $self->AddAttachment($attach);
}
-}
-
-=head2 AddAttachment $attachment
-
-Takes one attachment object of L<RT::Attachmment> class and attaches it to the message
-we're building.
-=cut
+ return $result;
-sub AddAttachment {
- my $self = shift;
- my $attach = shift;
- my $MIMEObj = shift || $self->TemplateObj->MIMEObj;
-
- $MIMEObj->attach(
- Type => $attach->ContentType,
- Charset => $attach->OriginalEncoding,
- Data => $attach->OriginalContent,
- Filename => $self->MIMEEncodeString( $attach->Filename ),
- 'RT-Attachment:' => $self->TicketObj->Id . "/"
- . $self->TransactionObj->Id . "/"
- . $attach->id,
- Encoding => '-SUGGEST',
- );
}
-=head2 AttachTickets [@IDs]
+# }}}
-Returns or set list of ticket's IDs that should be attached to an outgoing message.
+# }}}
-B<Note> this method works as a class method and setup things global, so you have to
-clean list by passing undef as argument.
-
-=cut
-
-{
- my $list = [];
-
- sub AttachTickets {
- my $self = shift;
- $list = [ grep defined, @_ ] if @_;
- return @$list;
- }
-}
-
-=head2 AddTickets
+# {{{ SendMessage
+=head2 SendMessage MIMEObj
-Attaches tickets to the current message, list of tickets' ids get from
-L</AttachTickets> method.
+sends the message using RT's preferred API.
+TODO: Break this out to a seperate module
=cut
-sub AddTickets {
+sub SendMessage {
my $self = shift;
- $self->AddTicket($_) foreach $self->AttachTickets;
- return;
-}
-
-=head2 AddTicket $ID
+ my $MIMEObj = shift;
-Attaches a ticket with ID to the message.
+ my $msgid = $MIMEObj->head->get('Message-Id');
-Each ticket is attached as multipart entity and all its messages and attachments
-are attached as sub entities in order of creation, but only if transaction type
-is Create or Correspond.
-=cut
-
-sub AddTicket {
- my $self = shift;
- my $tid = shift;
-
- # XXX: we need a current user here, but who is current user?
- my $attachs = RT::Attachments->new($RT::SystemUser);
- my $txn_alias = $attachs->TransactionAlias;
- $attachs->Limit( ALIAS => $txn_alias, FIELD => 'Type', VALUE => 'Create' );
- $attachs->Limit(
- ALIAS => $txn_alias,
- FIELD => 'Type',
- VALUE => 'Correspond'
- );
- $attachs->LimitByTicket($tid);
- $attachs->LimitNotEmpty;
- $attachs->OrderBy( FIELD => 'Created' );
-
- my $ticket_mime = MIME::Entity->build(
- Type => 'multipart/mixed',
- Top => 0,
- Description => "ticket #$tid",
- );
- while ( my $attachment = $attachs->Next ) {
- $self->AddAttachment( $attachment, $ticket_mime );
+ #If we don't have any recipients to send to, don't send a message;
+ unless ( $MIMEObj->head->get('To')
+ || $MIMEObj->head->get('Cc')
+ || $MIMEObj->head->get('Bcc') ) {
+ $RT::Logger->info($msgid. " No recipients found. Not sending.\n");
+ return (1);
}
- if ( $ticket_mime->parts ) {
- my $email_mime = $self->TemplateObj->MIMEObj;
- $email_mime->make_multipart;
- $email_mime->add_part($ticket_mime);
- }
- return;
-}
-=head2 RecordOutgoingMailTransaction MIMEObj
+ # PseudoTo (fake to headers) shouldn't get matched for message recipients.
+ # If we don't have any 'To' header, drop in the pseudo-to header.
-Record a transaction in RT with this outgoing message for future record-keeping purposes
-
-=cut
+ $self->SetHeader( 'To', join ( ',', @{ $self->{'PseudoTo'} } ) )
+ if ( $self->{'PseudoTo'} && ( @{ $self->{'PseudoTo'} } )
+ and ( !$MIMEObj->head->get('To') ) );
+ if ( $RT::MailCommand eq 'sendmailpipe' ) {
+ eval {
+ open( MAIL, "|$RT::SendmailPath $RT::SendmailArguments" );
+ print MAIL $MIMEObj->as_string;
+ close(MAIL);
+ };
+ if ($@) {
+ $RT::Logger->crit($msgid. "Could not send mail. -".$@ );
+ }
+ }
+ else {
+ my @mailer_args = ($RT::MailCommand);
+ local $ENV{MAILADDRESS};
-sub RecordOutgoingMailTransaction {
- my $self = shift;
- my $MIMEObj = shift;
+ if ( $RT::MailCommand eq 'sendmail' ) {
+ push @mailer_args, $RT::SendmailArguments;
+ }
+ elsif ( $RT::MailCommand eq 'smtp' ) {
+ $ENV{MAILADDRESS} = $RT::SMTPFrom || $MIMEObj->head->get('From');
+ push @mailer_args, (Server => $RT::SMTPServer);
+ push @mailer_args, (Debug => $RT::SMTPDebug);
+ }
+ else {
+ push @mailer_args, $RT::MailParams;
+ }
- my @parts = $MIMEObj->parts;
- my @attachments;
- my @keep;
- foreach my $part (@parts) {
- my $attach = $part->head->get('RT-Attachment');
- if ($attach) {
- $RT::Logger->debug(
- "We found an attachment. we want to not record it.");
- push @attachments, $attach;
- } else {
- $RT::Logger->debug("We found a part. we want to record it.");
- push @keep, $part;
+ unless ( $MIMEObj->send( @mailer_args ) ) {
+ $RT::Logger->crit($msgid. "Could not send mail." );
+ return (0);
}
}
- $MIMEObj->parts( \@keep );
- foreach my $attachment (@attachments) {
- $MIMEObj->head->add( 'RT-Attachment', $attachment );
- }
- RT::I18N::SetMIMEEntityToEncoding( $MIMEObj, 'utf-8', 'mime_words_ok' );
- my $transaction
- = RT::Transaction->new( $self->TransactionObj->CurrentUser );
-
-# XXX: TODO -> Record attachments as references to things in the attachments table, maybe.
+ my $success = ($msgid. " sent To: ".$MIMEObj->head->get('To') . " Cc: ".$MIMEObj->head->get('Cc') . " Bcc: ".$MIMEObj->head->get('Bcc'));
+ $success =~ s/\n//gi;
+ $RT::Logger->info($success);
- my $type;
- if ( $self->TransactionObj->Type eq 'Comment' ) {
- $type = 'CommentEmailRecord';
- } else {
- $type = 'EmailRecord';
- }
+ return (1);
+}
- my $msgid = $MIMEObj->head->get('Message-ID');
- chomp $msgid;
+# }}}
- my ( $id, $msg ) = $transaction->Create(
- Ticket => $self->TicketObj->Id,
- Type => $type,
- Data => $msgid,
- MIMEObj => $MIMEObj,
- ActivateScrips => 0
- );
+# {{{ Deal with message headers (Set* subs, designed for easy overriding)
- if ($id) {
- $self->{'OutgoingMailTransaction'} = $id;
- } else {
- $RT::Logger->warning(
- "Could not record outgoing message transaction: $msg");
- }
- return $id;
-}
+# {{{ sub SetRTSpecialHeaders
=head2 SetRTSpecialHeaders
@@ -567,267 +320,85 @@ that don't matter much to anybody else.
sub SetRTSpecialHeaders {
my $self = shift;
- $self->SetSubject();
- $self->SetSubjectToken();
- $self->SetHeaderAsEncoding( 'Subject',
- RT->Config->Get('EmailOutputEncoding') )
- if ( RT->Config->Get('EmailOutputEncoding') );
- $self->SetReturnAddress();
- $self->SetReferencesHeaders();
-
- unless ( $self->TemplateObj->MIMEObj->head->get('Message-ID') ) {
-
- # Get Message-ID for this txn
- my $msgid = "";
- if ( my $msg = $self->TransactionObj->Message->First ) {
- $msgid = $msg->GetHeader("RT-Message-ID")
- || $msg->GetHeader("Message-ID");
- }
+ $self->SetReferences();
- # If there is one, and we can parse it, then base our Message-ID on it
- if ( $msgid
- and $msgid
- =~ s/<(rt-.*?-\d+-\d+)\.(\d+)-\d+-\d+\@\QRT->Config->Get('Organization')\E>$/
- "<$1." . $self->TicketObj->id
- . "-" . $self->ScripObj->id
- . "-" . $self->ScripActionObj->{_Message_ID}
- . "@" . RT->Config->Get('Organization') . ">"/eg
- and $2 == $self->TicketObj->id
- )
- {
- $self->SetHeader( "Message-ID" => $msgid );
- } else {
- $self->SetHeader(
- 'Message-ID' => RT::Interface::Email::GenMessageId(
- Ticket => $self->TicketObj,
- Scrip => $self->ScripObj,
- ScripAction => $self->ScripActionObj
- ),
- );
- }
- }
+ $self->SetMessageID();
- $self->SetHeader( 'Precedence', "bulk" )
- unless ( $self->TemplateObj->MIMEObj->head->get("Precedence") );
+ $self->SetPrecedence();
- $self->SetHeader( 'X-RT-Loop-Prevention', RT->Config->Get('rtname') );
+ $self->SetHeader( 'X-RT-Loop-Prevention', $RT::rtname );
$self->SetHeader( 'RT-Ticket',
- RT->Config->Get('rtname') . " #" . $self->TicketObj->id() );
+ $RT::rtname . " #" . $self->TicketObj->id() );
$self->SetHeader( 'Managed-by',
- "RT $RT::VERSION (http://www.bestpractical.com/rt/)" );
+ "RT $RT::VERSION (http://www.bestpractical.com/rt/)" );
-# XXX, TODO: use /ShowUser/ShowUserEntry(or something like that) when it would be
-# refactored into user's method.
- if ( my $email = $self->TransactionObj->CreatorObj->EmailAddress ) {
- $self->SetHeader( 'RT-Originator', $email );
- }
+ $self->SetHeader( 'RT-Originator',
+ $self->TransactionObj->CreatorObj->EmailAddress );
+ return ();
}
+# {{{ sub SetReferences
-sub DeferDigestRecipients {
- my $self = shift;
- $RT::Logger->debug( "Calling SetRecipientDigests for transaction " . $self->TransactionObj . ", id " . $self->TransactionObj->id );
-
- # The digest attribute will be an array of notifications that need to
- # be sent for this transaction. The array will have the following
- # format for its objects.
- # $digest_hash -> {daily|weekly|susp} -> address -> {To|Cc|Bcc}
- # -> sent -> {true|false}
- # The "sent" flag will be used by the cron job to indicate that it has
- # run on this transaction.
- # In a perfect world we might move this hash construction to the
- # extension module itself.
- my $digest_hash = {};
-
- foreach my $mailfield (@EMAIL_RECIPIENT_HEADERS) {
- # If we have a "PseudoTo", the "To" contains it, so we don't need to access it
- next if ( ( $self->{'PseudoTo'} && @{ $self->{'PseudoTo'} } ) && ( $mailfield eq 'To' ) );
- $RT::Logger->debug( "Working on mailfield $mailfield; recipients are " . join( ',', @{ $self->{$mailfield} } ) );
-
- # Store the 'daily digest' folk in an array.
- my ( @send_now, @daily_digest, @weekly_digest, @suspended );
-
- # Have to get the list of addresses directly from the MIME header
- # at this point.
- $RT::Logger->debug( $self->TemplateObj->MIMEObj->head->as_string );
- foreach my $rcpt ( map { $_->address } $self->AddressesFromHeader($mailfield) ) {
- next unless $rcpt;
- my $user_obj = RT::User->new($RT::SystemUser);
- $user_obj->LoadByEmail($rcpt);
- if ( ! $user_obj->id ) {
- # If there's an email address in here without an associated
- # RT user, pass it on through.
- $RT::Logger->debug( "User $rcpt is not associated with an RT user object. Send mail.");
- push( @send_now, $rcpt );
- next;
- }
-
- my $mailpref = RT->Config->Get( 'EmailFrequency', $user_obj ) || '';
- $RT::Logger->debug( "Got user mail preference '$mailpref' for user $rcpt");
-
- if ( $mailpref =~ /daily/i ) { push( @daily_digest, $rcpt ) }
- elsif ( $mailpref =~ /weekly/i ) { push( @weekly_digest, $rcpt ) }
- elsif ( $mailpref =~ /suspend/i ) { push( @suspended, $rcpt ) }
- else { push( @send_now, $rcpt ) }
- }
-
- # Reset the relevant mail field.
- $RT::Logger->debug( "Removing deferred recipients from $mailfield: line");
- if (@send_now) {
- $self->SetHeader( $mailfield, join( ', ', @send_now ) );
- } else { # No recipients! Remove the header.
- $self->TemplateObj->MIMEObj->head->delete($mailfield);
- }
-
- # Push the deferred addresses into the appropriate field in
- # our attribute hash, with the appropriate mail header.
- $RT::Logger->debug(
- "Setting deferred recipients for attribute creation");
- $digest_hash->{'daily'}->{$_} = {'header' => $mailfield , _sent => 0} for (@daily_digest);
- $digest_hash->{'weekly'}->{$_} ={'header' => $mailfield, _sent => 0} for (@weekly_digest);
- $digest_hash->{'susp'}->{$_} = {'header' => $mailfield, _sent =>0 } for (@suspended);
- }
-
- if ( scalar keys %$digest_hash ) {
-
- # Save the hash so that we can add it as an attribute to the
- # outgoing email transaction.
- $self->{'Deferred'} = $digest_hash;
- } else {
- $RT::Logger->debug( "No recipients found for deferred delivery on "
- . "transaction #"
- . $self->TransactionObj->id );
- }
-}
+=head2 SetReferences
+
+ # This routine will set the References: and In-Reply-To headers,
+# autopopulating it with all the correspondence on this ticket so
+# far. This should make RT responses threadable.
+=cut
-
-sub RecordDeferredRecipients {
+sub SetReferences {
my $self = shift;
- return unless exists $self->{'Deferred'};
- my $txn_id = $self->{'OutgoingMailTransaction'};
- return unless $txn_id;
+ # TODO: this one is broken. What is this email really a reply to?
+ # If it's a reply to an incoming message, we'll need to use the
+ # actual message-id from the appropriate Attachment object. For
+ # incoming mails, we would like to preserve the In-Reply-To and/or
+ # References.
- my $txn_obj = RT::Transaction->new( $self->CurrentUser );
- $txn_obj->Load( $txn_id );
- my( $ret, $msg ) = $txn_obj->AddAttribute(
- Name => 'DeferredRecipients',
- Content => $self->{'Deferred'}
- );
- $RT::Logger->warning( "Unable to add deferred recipients to outgoing transaction: $msg" )
- unless $ret;
+ $self->SetHeader( 'In-Reply-To',
+ "<rt-" . $self->TicketObj->id() . "\@" . $RT::rtname . ">" );
- return ($ret,$msg);
+ # TODO We should always add References headers for all message-ids
+ # of previous messages related to this ticket.
}
-=head2 SquelchMailTo [@ADDRESSES]
-
-Mark ADDRESSES to be removed from list of the recipients. Returns list of the addresses.
-To empty list pass undefined argument.
-
-B<Note> that this method can be called as class method and works globaly. Don't forget to
-clean this list when blocking is not required anymore, pass undef to do this.
-
-=cut
-
-{
- my $squelch = [];
+# }}}
- sub SquelchMailTo {
- my $self = shift;
- if (@_) {
- $squelch = [ grep defined, @_ ];
- }
- return @$squelch;
- }
-}
+# {{{ sub SetMessageID
-=head2 RemoveInappropriateRecipients
+=head2 SetMessageID
-Remove addresses that are RT addresses or that are on this transaction's blacklist
+Without this one, threading won't work very nice in email agents.
+Anyway, I'm not really sure it's that healthy if we need to send
+several separate/different emails about the same transaction.
=cut
-sub RemoveInappropriateRecipients {
+sub SetMessageID {
my $self = shift;
- my @blacklist = ();
-
- # If there are no recipients, don't try to send the message.
- # If the transaction has content and has the header RT-Squelch-Replies-To
-
- my $msgid = $self->TemplateObj->MIMEObj->head->get('Message-Id');
- if ( my $attachment = $self->TransactionObj->Attachments->First ) {
-
- if ( $attachment->GetHeader('RT-DetectedAutoGenerated') ) {
-
- # What do we want to do with this? It's probably (?) a bounce
- # caused by one of the watcher addresses being broken.
- # Default ("true") is to redistribute, for historical reasons.
-
- if ( !RT->Config->Get('RedistributeAutoGeneratedMessages') ) {
-
- # Don't send to any watchers.
- @{ $self->{$_} } = () for (@EMAIL_RECIPIENT_HEADERS);
- $RT::Logger->info( $msgid
- . " The incoming message was autogenerated. "
- . "Not redistributing this message based on site configuration."
- );
- } elsif ( RT->Config->Get('RedistributeAutoGeneratedMessages') eq
- 'privileged' )
- {
-
- # Only send to "privileged" watchers.
- foreach my $type (@EMAIL_RECIPIENT_HEADERS) {
- foreach my $addr ( @{ $self->{$type} } ) {
- my $user = RT::User->new($RT::SystemUser);
- $user->LoadByEmail($addr);
- push @blacklist, $addr if ( !$user->Privileged );
- }
- }
- $RT::Logger->info( $msgid
- . " The incoming message was autogenerated. "
- . "Not redistributing this message to unprivileged users based on site configuration."
- );
- }
- }
-
- if ( my $squelch = $attachment->GetHeader('RT-Squelch-Replies-To') ) {
- push @blacklist, split( /,/, $squelch );
- }
- }
-
-# Let's grab the SquelchMailTo attribue and push those entries into the @blacklist
- push @blacklist, map $_->Content, $self->TicketObj->SquelchMailTo;
- push @blacklist, $self->SquelchMailTo;
-
- # Cycle through the people we're sending to and pull out anyone on the
- # system blacklist
+ # TODO this one might be sort of broken. If we have several scrips +++
+ # sending several emails to several different persons, we need to
+ # pull out different message-ids. I'd suggest message ids like
+ # "rt-ticket#-transaction#-scrip#-receipient#"
+
+ $self->SetHeader( 'Message-ID',
+ "<rt-"
+ . $RT::VERSION ."-"
+ . $self->TicketObj->id() . "-"
+ . $self->TransactionObj->id() . "."
+ . rand(20) . "\@"
+ . $RT::Organization . ">" )
+ unless $self->TemplateObj->MIMEObj->head->get('Message-ID');
+}
- # Trim leading and trailing spaces.
- @blacklist = map { RT::User->CanonicalizeEmailAddress( $_->address ) } Email::Address->parse(join(', ', grep {defined} @blacklist));
+# }}}
- foreach my $type (@EMAIL_RECIPIENT_HEADERS) {
- my @addrs;
- foreach my $addr ( @{ $self->{$type} } ) {
+# }}}
- # Weed out any RT addresses. We really don't want to talk to ourselves!
- # If we get a reply back, that means it's not an RT address
- if ( !RT::EmailParser->CullRTAddresses($addr) ) {
- $RT::Logger->info( $msgid . "$addr appears to point to this RT instance. Skipping" );
- next;
- }
- if ( grep /^\Q$addr\E$/, @blacklist ) {
- $RT::Logger->info( $msgid . "$addr was blacklisted for outbound mail on this transaction. Skipping");
- next;
- }
- push @addrs, $addr;
- }
- @{ $self->{$type} } = @addrs;
- }
-}
+# {{{ sub SetReturnAddress
=head2 SetReturnAddress is_comment => BOOLEAN
@@ -838,11 +409,8 @@ Calculate and set From and Reply-To headers based on the is_comment flag.
sub SetReturnAddress {
my $self = shift;
- my %args = (
- is_comment => 0,
- friendly_name => undef,
- @_
- );
+ my %args = ( is_comment => 0,
+ @_ );
# From and Reply-To
# $args{is_comment} should be set if the comment address is to be used.
@@ -850,37 +418,29 @@ sub SetReturnAddress {
if ( $args{'is_comment'} ) {
$replyto = $self->TicketObj->QueueObj->CommentAddress
- || RT->Config->Get('CommentAddress');
- } else {
+ || $RT::CommentAddress;
+ }
+ else {
$replyto = $self->TicketObj->QueueObj->CorrespondAddress
- || RT->Config->Get('CorrespondAddress');
+ || $RT::CorrespondAddress;
}
unless ( $self->TemplateObj->MIMEObj->head->get('From') ) {
- if ( RT->Config->Get('UseFriendlyFromLine') ) {
- my $friendly_name = $args{friendly_name};
-
- unless ( $friendly_name ) {
- $friendly_name = $self->TransactionObj->CreatorObj->FriendlyName;
- if ( $friendly_name =~ /^"(.*)"$/ ) { # a quoted string
- $friendly_name = $1;
- }
- }
-
- $friendly_name =~ s/"/\\"/g;
- $self->SetHeader(
- 'From',
- sprintf(
- RT->Config->Get('FriendlyFromLineFormat'),
- $self->MIMEEncodeString(
- $friendly_name, RT->Config->Get('EmailOutputEncoding')
- ),
- $replyto
- ),
- );
- } else {
- $self->SetHeader( 'From', $replyto );
- }
+ if ($RT::UseFriendlyFromLine) {
+ my $friendly_name = $self->TransactionObj->CreatorObj->RealName;
+ if ( $friendly_name =~ /^"(.*)"$/ ) { # a quoted string
+ $friendly_name = $1;
+ }
+
+ $friendly_name =~ s/"/\\"/g;
+ $self->SetHeader( 'From',
+ sprintf($RT::FriendlyFromLineFormat,
+ $self->MIMEEncodeString( $friendly_name, $RT::EmailOutputEncoding ), $replyto),
+ );
+ }
+ else {
+ $self->SetHeader( 'From', $replyto );
+ }
}
unless ( $self->TemplateObj->MIMEObj->head->get('Reply-To') ) {
@@ -889,6 +449,10 @@ sub SetReturnAddress {
}
+# }}}
+
+# {{{ sub SetHeader
+
=head2 SetHeader FIELD, VALUE
Set the FIELD of the current MIME object into VALUE.
@@ -902,151 +466,163 @@ sub SetHeader {
chomp $val;
chomp $field;
- my $head = $self->TemplateObj->MIMEObj->head;
- $head->fold_length( $field, 10000 );
- $head->replace( $field, $val );
- return $head->get($field);
+ $self->TemplateObj->MIMEObj->head->fold_length( $field, 10000 );
+ $self->TemplateObj->MIMEObj->head->replace( $field, $val );
+ return $self->TemplateObj->MIMEObj->head->get($field);
}
-=head2 SetSubject
+# }}}
-This routine sets the subject. it does not add the rt tag. That gets done elsewhere
-If subject is already defined via template, it uses that. otherwise, it tries to get
-the transaction's subject.
+# {{{ sub SetRecipients
-=cut
+=head2 SetRecipients
-sub SetSubject {
+Dummy method to be overriden by subclasses which want to set the recipients.
+
+=cut
+
+sub SetRecipients {
my $self = shift;
- my $subject;
+ return ();
+}
- if ( $self->TemplateObj->MIMEObj->head->get('Subject') ) {
- return ();
- }
+# }}}
- my $message = $self->TransactionObj->Attachments;
- $message->RowsPerPage(1);
- if ( $self->{'Subject'} ) {
- $subject = $self->{'Subject'};
- } elsif ( my $first = $message->First ) {
- my $tmp = $first->GetHeader('Subject');
- $subject = defined $tmp ? $tmp : $self->TicketObj->Subject;
- } else {
- $subject = $self->TicketObj->Subject;
- }
- $subject = '' unless defined $subject;
- chomp $subject;
+# {{{ sub SetTo
- $subject =~ s/(\r\n|\n|\s)/ /g;
+=head2 SetTo
- $self->SetHeader( 'Subject', $subject );
+Takes a string that is the addresses you want to send mail to
+=cut
+
+sub SetTo {
+ my $self = shift;
+ my $addresses = shift;
+ return $self->SetHeader( 'To', $addresses );
}
-=head2 SetSubjectToken
+# }}}
-This routine fixes the RT tag in the subject. It's unlikely that you want to overwrite this.
+# {{{ sub SetCc
+
+=head2 SetCc
+
+Takes a string that is the addresses you want to Cc
=cut
-sub SetSubjectToken {
- my $self = shift;
+sub SetCc {
+ my $self = shift;
+ my $addresses = shift;
- my $head = $self->TemplateObj->MIMEObj->head;
- $head->replace(
- Subject => RT::Interface::Email::AddSubjectTag(
- Encode::decode_utf8( $head->get('Subject') ),
- $self->TicketObj,
- ),
- );
+ return $self->SetHeader( 'Cc', $addresses );
}
-=head2 SetReferencesHeaders
+# }}}
+
+# {{{ sub SetBcc
-Set References and In-Reply-To headers for this message.
+=head2 SetBcc
+
+Takes a string that is the addresses you want to Bcc
=cut
-sub SetReferencesHeaders {
+sub SetBcc {
+ my $self = shift;
+ my $addresses = shift;
+
+ return $self->SetHeader( 'Bcc', $addresses );
+}
+
+# }}}
+
+# {{{ sub SetPrecedence
+
+sub SetPrecedence {
my $self = shift;
- my ( @in_reply_to, @references, @msgid );
-
- if ( my $top = $self->TransactionObj->Message->First ) {
- @in_reply_to = split( /\s+/m, $top->GetHeader('In-Reply-To') || '' );
- @references = split( /\s+/m, $top->GetHeader('References') || '' );
- @msgid = split( /\s+/m, $top->GetHeader('Message-ID') || '' );
- } else {
- return (undef);
+
+ unless ( $self->TemplateObj->MIMEObj->head->get("Precedence") ) {
+ $self->SetHeader( 'Precedence', "bulk" );
}
+}
- # There are two main cases -- this transaction was created with
- # the RT Web UI, and hence we want to *not* append its Message-ID
- # to the References and In-Reply-To. OR it came from an outside
- # source, and we should treat it as per the RFC
- my $org = RT->Config->Get('Organization');
- if ( "@msgid" =~ /<(rt-.*?-\d+-\d+)\.(\d+)-0-0\@\Q$org\E>/ ) {
-
- # Make all references which are internal be to version which we
- # have sent out
-
- for ( @references, @in_reply_to ) {
- s/<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@\Q$org\E>$/
- "<$1." . $self->TicketObj->id .
- "-" . $self->ScripObj->id .
- "-" . $self->ScripActionObj->{_Message_ID} .
- "@" . $org . ">"/eg
- }
+# }}}
- # In reply to whatever the internal message was in reply to
- $self->SetHeader( 'In-Reply-To', join( " ", (@in_reply_to) ) );
+# {{{ sub SetSubject
+
+=head2 SetSubject
- # Default the references to whatever we're in reply to
- @references = @in_reply_to unless @references;
+This routine sets the subject. it does not add the rt tag. that gets done elsewhere
+If $self->{'Subject'} is already defined, it uses that. otherwise, it tries to get
+the transaction's subject.
- # References are unchanged from internal
- } else {
+=cut
- # In reply to that message
- $self->SetHeader( 'In-Reply-To', join( " ", (@msgid) ) );
+sub SetSubject {
+ my $self = shift;
+ my $subject;
- # Default the references to whatever we're in reply to
- @references = @in_reply_to unless @references;
+ unless ( $self->TemplateObj->MIMEObj->head->get('Subject') ) {
+ my $message = $self->TransactionObj->Attachments;
+ my $ticket = $self->TicketObj->Id;
- # Push that message onto the end of the references
- push @references, @msgid;
- }
+ if ( $self->{'Subject'} ) {
+ $subject = $self->{'Subject'};
+ }
+ elsif ( ( $message->First() )
+ && ( $message->First->Headers ) ) {
+ my $header = $message->First->Headers();
+ $header =~ s/\n\s+/ /g;
+ if ( $header =~ /^Subject: (.*?)$/m ) {
+ $subject = $1;
+ }
+ else {
+ $subject = $self->TicketObj->Subject();
+ }
- # Push pseudo-ref to the front
- my $pseudo_ref = $self->PseudoReference;
- @references = ( $pseudo_ref, grep { $_ ne $pseudo_ref } @references );
+ }
+ else {
+ $subject = $self->TicketObj->Subject();
+ }
- # If there are more than 10 references headers, remove all but the
- # first four and the last six (Gotta keep this from growing
- # forever)
- splice( @references, 4, -6 ) if ( $#references >= 10 );
+ $subject =~ s/(\r\n|\n|\s)/ /gi;
- # Add on the references
- $self->SetHeader( 'References', join( " ", @references ) );
- $self->TemplateObj->MIMEObj->head->fold_length( 'References', 80 );
+ chomp $subject;
+ $self->SetHeader( 'Subject', $subject );
+ }
+ return ($subject);
}
-=head2 PseudoReference
+# }}}
-Returns a fake Message-ID: header for the ticket to allow a base level of threading
+# {{{ sub SetSubjectToken
-=cut
+=head2 SetSubjectToken
+
+This routine fixes the RT tag in the subject. It's unlikely that you want to overwrite this.
-sub PseudoReference {
+=cut
+sub SetSubjectToken {
my $self = shift;
- my $pseudo_ref
- = '<RT-Ticket-'
- . $self->TicketObj->id . '@'
- . RT->Config->Get('Organization') . '>';
- return $pseudo_ref;
+ my $tag = "[$RT::rtname #" . $self->TicketObj->id . "]";
+ my $sub = $self->TemplateObj->MIMEObj->head->get('Subject');
+ unless ( $sub =~ /\Q$tag\E/ ) {
+ $sub =~ s/(\r\n|\n|\s)/ /gi;
+ chomp $sub;
+ $self->TemplateObj->MIMEObj->head->replace( 'Subject', "$tag $sub" );
+ }
}
+# }}}
+
+# }}}
+
+# {{{
+
=head2 SetHeaderAsEncoding($field_name, $charset_encoding)
This routine converts the field into specified charset encoding.
@@ -1057,37 +633,53 @@ sub SetHeaderAsEncoding {
my $self = shift;
my ( $field, $enc ) = ( shift, shift );
- my $head = $self->TemplateObj->MIMEObj->head;
-
- if ( lc($field) eq 'from' and RT->Config->Get('SMTPFrom') ) {
- $head->replace( $field, RT->Config->Get('SMTPFrom') );
- return;
+ if ($field eq 'From' and $RT::SMTPFrom) {
+ $self->TemplateObj->MIMEObj->head->replace( $field, $RT::SMTPFrom );
+ return;
}
- my $value = $head->get( $field );
- $value = $self->MIMEEncodeString( $value, $enc );
- $head->replace( $field, $value );
+ my $value = $self->TemplateObj->MIMEObj->head->get($field);
-}
+ # don't bother if it's us-ascii
+
+ # See RT::I18N, 'NOTES: Why Encode::_utf8_off before Encode::from_to'
-=head2 MIMEEncodeString
+ $value = $self->MIMEEncodeString($value, $enc);
-Takes a perl string and optional encoding pass it over
-L<RT::Interface::Email/EncodeToMIME>.
+ $self->TemplateObj->MIMEObj->head->replace( $field, $value );
-Basicly encode a string using B encoding according to RFC2047.
+
+}
+# }}}
+
+# {{{ MIMENcodeString
+
+=head2 MIMEEncodeString STRING ENCODING
+
+Takes a string and a possible encoding and returns the string wrapped in MIME goo.
=cut
sub MIMEEncodeString {
- my $self = shift;
- return RT::Interface::Email::EncodeToMIME( String => $_[0], Charset => $_[1] );
+ my $self = shift;
+ my $value = shift;
+ my $enc = shift;
+
+ chomp $value;
+ return ($value) unless $value =~ /[^\x20-\x7e]/;
+
+ $value =~ s/\s*$//;
+ Encode::_utf8_off($value);
+ my $res = Encode::from_to( $value, "utf-8", $enc );
+ $value = encode_mimeword( $value, 'B', $enc );
}
+# }}}
+
eval "require RT::Action::SendEmail_Vendor";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Vendor.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Vendor.pm});
eval "require RT::Action::SendEmail_Local";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Local.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Action/SendEmail_Local.pm});
1;
diff --git a/rt/lib/RT/Attachment.pm b/rt/lib/RT/Attachment.pm
index 4327238e6..2ed520162 100755
--- a/rt/lib/RT/Attachment.pm
+++ b/rt/lib/RT/Attachment.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -135,7 +110,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -144,14 +119,14 @@ Returns the current value of id.
=cut
-=head2 TransactionId
+=item TransactionId
Returns the current value of TransactionId.
(In the database, TransactionId is stored as int(11).)
-=head2 SetTransactionId VALUE
+=item SetTransactionId VALUE
Set TransactionId to VALUE.
@@ -162,14 +137,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Parent
+=item Parent
Returns the current value of Parent.
(In the database, Parent is stored as int(11).)
-=head2 SetParent VALUE
+=item SetParent VALUE
Set Parent to VALUE.
@@ -180,14 +155,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 MessageId
+=item MessageId
Returns the current value of MessageId.
(In the database, MessageId is stored as varchar(160).)
-=head2 SetMessageId VALUE
+=item SetMessageId VALUE
Set MessageId to VALUE.
@@ -198,14 +173,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Subject
+=item Subject
Returns the current value of Subject.
(In the database, Subject is stored as varchar(255).)
-=head2 SetSubject VALUE
+=item SetSubject VALUE
Set Subject to VALUE.
@@ -216,14 +191,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Filename
+=item Filename
Returns the current value of Filename.
(In the database, Filename is stored as varchar(255).)
-=head2 SetFilename VALUE
+=item SetFilename VALUE
Set Filename to VALUE.
@@ -234,14 +209,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ContentType
+=item ContentType
Returns the current value of ContentType.
(In the database, ContentType is stored as varchar(80).)
-=head2 SetContentType VALUE
+=item SetContentType VALUE
Set ContentType to VALUE.
@@ -252,14 +227,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ContentEncoding
+=item ContentEncoding
Returns the current value of ContentEncoding.
(In the database, ContentEncoding is stored as varchar(80).)
-=head2 SetContentEncoding VALUE
+=item SetContentEncoding VALUE
Set ContentEncoding to VALUE.
@@ -270,14 +245,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Content
+=item Content
Returns the current value of Content.
(In the database, Content is stored as longtext.)
-=head2 SetContent VALUE
+=item SetContent VALUE
Set Content to VALUE.
@@ -288,14 +263,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Headers
+=item Headers
Returns the current value of Headers.
(In the database, Headers is stored as longtext.)
-=head2 SetHeaders VALUE
+=item SetHeaders VALUE
Set Headers to VALUE.
@@ -306,7 +281,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -315,7 +290,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -325,33 +300,33 @@ Returns the current value of Created.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
TransactionId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Parent =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
MessageId =>
- {read => 1, write => 1, sql_type => 12, length => 160, is_blob => 0, is_numeric => 0, type => 'varchar(160)', default => ''},
+ {read => 1, write => 1, type => 'varchar(160)', default => ''},
Subject =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
Filename =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
ContentType =>
- {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
+ {read => 1, write => 1, type => 'varchar(80)', default => ''},
ContentEncoding =>
- {read => 1, write => 1, sql_type => 12, length => 80, is_blob => 0, is_numeric => 0, type => 'varchar(80)', default => ''},
+ {read => 1, write => 1, type => 'varchar(80)', default => ''},
Content =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'longtext', default => ''},
+ {read => 1, write => 1, type => 'longtext', default => ''},
Headers =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'longtext', default => ''},
+ {read => 1, write => 1, type => 'longtext', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -383,7 +358,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Attachments.pm b/rt/lib/RT/Attachments.pm
index 416cde6ba..177cdd094 100755
--- a/rt/lib/RT/Attachments.pm
+++ b/rt/lib/RT/Attachments.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Attachment item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Condition/AnyTransaction.pm b/rt/lib/RT/Condition/AnyTransaction.pm
index 1b90aa53e..4519fcf5a 100644
--- a/rt/lib/RT/Condition/AnyTransaction.pm
+++ b/rt/lib/RT/Condition/AnyTransaction.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,36 +14,21 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
+# 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.
#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
#
-# END BPS TAGGED BLOCK }}}
+# END LICENSE BLOCK
+
package RT::Condition::AnyTransaction;
-use base 'RT::Condition';
+require RT::Condition::Generic;
use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
=head2 IsApplicable
diff --git a/rt/lib/RT/Condition/Generic.pm b/rt/lib/RT/Condition/Generic.pm
index 08baeda25..bd269315e 100755
--- a/rt/lib/RT/Condition/Generic.pm
+++ b/rt/lib/RT/Condition/Generic.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,61 +14,198 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
=head1 NAME
- RT::Condition::Generic - deprecated, see RT::Condition
+ RT::Condition::Generic - ;
=head1 SYNOPSIS
- use RT::Condition::Generic;
+ use RT::Condition::Generic;
+ my $foo = new RT::Condition::IsApplicable(
+ TransactionObj => $tr,
+ TicketObj => $ti,
+ ScripObj => $scr,
+ Argument => $arg,
+ Type => $type);
+
+ if ($foo->IsApplicable) {
+ # do something
+ }
+
=head1 DESCRIPTION
-This module is provided only for backwards compatibility.
=head1 METHODS
+=begin testing
+
+ok (require RT::Condition::Generic);
+
+=end testing
+
+
=cut
-use strict;
-use warnings;
package RT::Condition::Generic;
-use base 'RT::Condition';
+
+use RT::Base;
+use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Base);
+
+# {{{ sub new
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->_Init(@_);
+ return $self;
+}
+# }}}
+
+# {{{ sub _Init
+sub _Init {
+ my $self = shift;
+ my %args = ( TransactionObj => undef,
+ TicketObj => undef,
+ ScripObj => undef,
+ TemplateObj => undef,
+ Argument => undef,
+ ApplicableTransTypes => undef,
+ @_ );
+
+ $self->{'Argument'} = $args{'Argument'};
+ $self->{'ScripObj'} = $args{'ScripObj'};
+ $self->{'TicketObj'} = $args{'TicketObj'};
+ $self->{'TransactionObj'} = $args{'TransactionObj'};
+ $self->{'ApplicableTransTypes'} = $args{'ApplicableTransTypes'};
+}
+# }}}
+
+# Access Scripwide data
+
+# {{{ sub Argument
+
+=head2 Argument
+
+Return the optional argument associated with this ScripCondition
+
+=cut
+
+sub Argument {
+ my $self = shift;
+ return($self->{'Argument'});
+}
+# }}}
+
+# {{{ sub TicketObj
+
+=head2 TicketObj
+
+Return the ticket object we're talking about
+
+=cut
+
+sub TicketObj {
+ my $self = shift;
+ return($self->{'TicketObj'});
+}
+# }}}
+
+# {{{ sub ScripObj
+
+=head2 ScripObj
+
+Return the Scrip object we're talking about
+
+=cut
+
+sub ScripObj {
+ my $self = shift;
+ return($self->{'ScripObj'});
+}
+# }}}
+# {{{ sub TransactionObj
+
+=head2 TransactionObj
+
+Return the transaction object we're talking about
+
+=cut
+
+sub TransactionObj {
+ my $self = shift;
+ return($self->{'TransactionObj'});
+}
+# }}}
+
+# {{{ sub Type
+
+=head2 Type
+
+
+
+=cut
+
+sub ApplicableTransTypes {
+ my $self = shift;
+ return($self->{'ApplicableTransTypes'});
+}
+# }}}
+
+
+# Scrip methods
+
+
+#What does this type of Action does
+
+# {{{ sub Describe
+sub Describe {
+ my $self = shift;
+ return ($self->loc("No description for [_1]", ref $self));
+}
+# }}}
+
+
+#Parse the templates, get things ready to go.
+
+#If this rule applies to this transaction, return true.
+
+# {{{ sub IsApplicable
+sub IsApplicable {
+ my $self = shift;
+ return(undef);
+}
+# }}}
+
+# {{{ sub DESTROY
+sub DESTROY {
+ my $self = shift;
+
+ # We need to clean up all the references that might maybe get
+ # oddly circular
+ $self->{'TemplateObj'} =undef
+ $self->{'TicketObj'} = undef;
+ $self->{'TransactionObj'} = undef;
+ $self->{'ScripObj'} = undef;
+
+}
+
+# }}}
eval "require RT::Condition::Generic_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/Generic_Vendor.pm});
-warn "RT::Condition::Generic has become RT::Condition. Please adjust your RT::Condition::Generic_Vendor file at " . $INC{"RT/Condition/Generic_Vendor.pm"} if !$@;
-
eval "require RT::Condition::Generic_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/Generic_Local.pm});
-warn "RT::Condition::Generic has become RT::Condition. Please adjust your RT::Condition::Generic_Local file at " . $INC{"RT/Condition/Generic_Local.pm"} if !$@;
1;
-
diff --git a/rt/lib/RT/Condition/StatusChange.pm b/rt/lib/RT/Condition/StatusChange.pm
index 285b71da6..8afabcda0 100644
--- a/rt/lib/RT/Condition/StatusChange.pm
+++ b/rt/lib/RT/Condition/StatusChange.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,35 +14,22 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
+# END LICENSE BLOCK
+
+
package RT::Condition::StatusChange;
-use base 'RT::Condition';
+require RT::Condition::Generic;
+
use strict;
+use vars qw/@ISA/;
+@ISA = qw(RT::Condition::Generic);
=head2 IsApplicable
diff --git a/rt/lib/RT/CurrentUser.pm b/rt/lib/RT/CurrentUser.pm
index b674d4e60..4ca2f9891 100755
--- a/rt/lib/RT/CurrentUser.pm
+++ b/rt/lib/RT/CurrentUser.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,165 +14,182 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
=head1 NAME
RT::CurrentUser - an RT object representing the current user
=head1 SYNOPSIS
- use RT::CurrentUser;
-
- # laod
- my $current_user = new RT::CurrentUser;
- $current_user->Load(...);
- # or
- my $current_user = RT::CurrentUser->new( $user_obj );
- # or
- my $current_user = RT::CurrentUser->new( $address || $name || $id );
-
- # manipulation
- $current_user->UserObj->SetName('new_name');
+ use RT::CurrentUser
=head1 DESCRIPTION
-B<Read-only> subclass of L<RT::User> class. Used to define the current
-user. You should pass an instance of this class to constructors of
-many RT classes, then the instance used to check ACLs and localize
-strings.
=head1 METHODS
-See also L<RT::User> for a list of methods this class has.
-=head2 new
+=begin testing
+
+ok (require RT::CurrentUser);
-Returns new CurrentUser object. Unlike all other classes of RT it takes
-either subclass of C<RT::User> class object or scalar value that is
-passed to Load method.
+=end testing
=cut
package RT::CurrentUser;
+use RT::Record;
use RT::I18N;
use strict;
-use warnings;
+use vars qw/@ISA/;
+@ISA= qw(RT::Record);
-use base qw/RT::User/;
+# {{{ sub _Init
#The basic idea here is that $self->CurrentUser is always supposed
# to be a CurrentUser object. but that's hard to do when we're trying to load
# the CurrentUser object
-sub _Init {
- my $self = shift;
- my $User = shift;
-
- $self->{'table'} = "Users";
-
- if ( defined $User ) {
-
- if ( UNIVERSAL::isa( $User, 'RT::User' ) ) {
- $self->LoadById( $User->id );
- }
- elsif ( ref $User ) {
- $RT::Logger->crit(
- "RT::CurrentUser->new() called with a bogus argument: $User");
- }
- else {
- $self->Load( $User );
- }
- }
+sub _Init {
+ my $self = shift;
+ my $Name = shift;
- $self->_BuildTableAttributes;
+ $self->{'table'} = "Users";
-}
+ if (defined($Name)) {
+ $self->Load($Name);
+ }
+
+ $self->CurrentUser($self);
-=head2 Create, Delete and Set*
+}
+# }}}
-As stated above it's a subclass of L<RT::User>, but this class is read-only
-and calls to these methods are illegal. Return 'permission denied' message
-and log an error.
-
-=cut
+# {{{ sub Create
sub Create {
my $self = shift;
- $RT::Logger->error('RT::CurrentUser is read-only, RT::User for manipulation');
return (0, $self->loc('Permission Denied'));
}
+# }}}
+
+# {{{ sub Delete
+
sub Delete {
my $self = shift;
- $RT::Logger->error('RT::CurrentUser is read-only, RT::User for manipulation');
return (0, $self->loc('Permission Denied'));
}
-sub _Set {
- my $self = shift;
- $RT::Logger->error('RT::CurrentUser is read-only, RT::User for manipulation');
- return (0, $self->loc('Permission Denied'));
-}
+# }}}
+
+# {{{ sub UserObj
=head2 UserObj
-Returns the L<RT::User> object associated with this CurrentUser object.
+ Returns the RT::User object associated with this CurrentUser object.
=cut
sub UserObj {
my $self = shift;
-
- my $user = RT::User->new( $self );
- unless ( $user->LoadById( $self->Id ) ) {
- $RT::Logger->error(
- $self->loc("Couldn't load [_1] from the users database.\n", $self->Id)
- );
+
+ unless ($self->{'UserObj'}) {
+ use RT::User;
+ $self->{'UserObj'} = RT::User->new($self);
+ unless ($self->{'UserObj'}->Load($self->Id)) {
+ $RT::Logger->err($self->loc("Couldn't load [_1] from the users database.\n", $self->Id));
+ }
+
}
- return $user;
+ return ($self->{'UserObj'});
}
+# }}}
-sub _CoreAccessible {
- {
- Name => { 'read' => 1 },
- Gecos => { 'read' => 1 },
- RealName => { 'read' => 1 },
- Lang => { 'read' => 1 },
- Password => { 'read' => 0, 'write' => 0 },
- EmailAddress => { 'read' => 1, 'write' => 0 }
- };
-
+# {{{ sub PrincipalObj
+
+=head2 PrincipalObj
+
+ Returns this user's principal object. this is just a helper routine for
+ $self->UserObj->PrincipalObj
+
+=cut
+
+sub PrincipalObj {
+ my $self = shift;
+ return($self->UserObj->PrincipalObj);
}
+
+# }}}
+
+
+# {{{ sub PrincipalId
+
+=head2 PrincipalId
+
+ Returns this user's principal Id. this is just a helper routine for
+ $self->UserObj->PrincipalId
+
+=cut
+
+sub PrincipalId {
+ my $self = shift;
+ return($self->UserObj->PrincipalId);
+}
+
+
+# }}}
+
+
+# {{{ sub _Accessible
+sub _Accessible {
+ my $self = shift;
+ my %Cols = (
+ Name => 'read',
+ Gecos => 'read',
+ RealName => 'read',
+ Password => 'neither',
+ EmailAddress => 'read',
+ Privileged => 'read',
+ IsAdministrator => 'read'
+ );
+ return($self->SUPER::_Accessible(@_, %Cols));
+}
+# }}}
+
+# {{{ sub LoadByEmail
+
+=head2 LoadByEmail
+
+Loads a User into this CurrentUser object.
+Takes the email address of the user to load.
+
+=cut
+
+sub LoadByEmail {
+ my $self = shift;
+ my $identifier = shift;
+
+ $identifier = RT::User::CanonicalizeEmailAddress(undef, $identifier);
+
+ $self->LoadByCol("EmailAddress",$identifier);
+
+}
+# }}}
+
+# {{{ sub LoadByGecos
+
=head2 LoadByGecos
Loads a User into this CurrentUser object.
@@ -188,128 +199,171 @@ Takes a unix username as its only argument.
sub LoadByGecos {
my $self = shift;
- return $self->LoadByCol( "Gecos", shift );
+ my $identifier = shift;
+
+ $self->LoadByCol("Gecos",$identifier);
+
}
+# }}}
+
+# {{{ sub LoadByName
=head2 LoadByName
Loads a User into this CurrentUser object.
Takes a Name.
-
=cut
sub LoadByName {
my $self = shift;
- return $self->LoadByCol( "Name", shift );
+ my $identifier = shift;
+ $self->LoadByCol("Name",$identifier);
+
}
+# }}}
-=head2 LanguageHandle
-
-Returns this current user's langauge handle. Should take a language
-specification. but currently doesn't
+# {{{ sub Load
-=cut
+=head2 Load
-sub LanguageHandle {
- my $self = shift;
- if ( !defined $self->{'LangHandle'}
- || !UNIVERSAL::can( $self->{'LangHandle'}, 'maketext' )
- || @_ )
- {
- if ( my $lang = $self->Lang ) {
- push @_, $lang;
- }
- elsif ( $self->id && ($self->id == ($RT::SystemUser->id||0) || $self->id == ($RT::Nobody->id||0)) ) {
- # don't use ENV magic for system users
- push @_, 'en';
- }
+Loads a User into this CurrentUser object.
+Takes either an integer (users id column reference) or a Name
+The latter is deprecated. Instead, you should use LoadByName.
+Formerly, this routine also took email addresses.
- $self->{'LangHandle'} = RT::I18N->get_handle(@_);
- }
+=cut
- # Fall back to english.
- unless ( $self->{'LangHandle'} ) {
- die "We couldn't get a dictionary. Ne mogu naidti slovar. No puedo encontrar dictionario.";
- }
- return $self->{'LangHandle'};
+sub Load {
+ my $self = shift;
+ my $identifier = shift;
+
+ #if it's an int, load by id. otherwise, load by name.
+ if ($identifier !~ /\D/) {
+ $self->SUPER::LoadById($identifier);
+ }
+ else {
+ # This is a bit dangerous, we might get false authen if somebody
+ # uses ambigous userids or real names:
+ $self->LoadByCol("Name",$identifier);
+ }
}
-sub loc {
- my $self = shift;
- return '' if !defined $_[0] || $_[0] eq '';
+# }}}
- my $handle = $self->LanguageHandle;
+# {{{ sub IsPassword
- if (@_ == 1) {
- # pre-scan the lexicon hashes to return _AUTO keys verbatim,
- # to keep locstrings containing '[' and '~' from tripping over Maketext
- return $_[0] unless grep exists $_->{$_[0]}, @{ $handle->_lex_refs };
- }
+=head2 IsPassword
- return $handle->maketext(@_);
-}
+Takes a password as a string. Passes it off to IsPassword in this
+user's UserObj. If it is the user's password and the user isn't
+disabled, returns 1.
-sub loc_fuzzy {
- my $self = shift;
- return '' if !defined $_[0] || $_[0] eq '';
+Otherwise, returns undef.
- # XXX: work around perl's deficiency when matching utf8 data
- return $_[0] if Encode::is_utf8($_[0]);
+=cut
- return $self->LanguageHandle->maketext_fuzzy( @_ );
+sub IsPassword {
+ my $self = shift;
+ my $value = shift;
+
+ return ($self->UserObj->IsPassword($value));
}
-=head2 CurrentUser
+# }}}
-Return the current currentuser object
+# {{{ sub Privileged
+
+=head2 Privileged
+
+Returns true if the current user can be granted rights and be
+a member of groups.
=cut
-sub CurrentUser {
+sub Privileged {
my $self = shift;
- return($self);
-
+ return ($self->UserObj->Privileged());
}
-=head2 Authenticate
+# }}}
-Takes $password, $created and $nonce, and returns a boolean value
-representing whether the authentication succeeded.
-If both $nonce and $created are specified, validate $password against:
+# {{{ sub HasRight
- encode_base64(sha1(
- $nonce .
- $created .
- sha1_hex( "$username:$realm:$server_pass" )
- ))
+=head2 HasRight
-where $server_pass is the md5_hex(password) digest stored in the
-database, $created is in ISO time format, and $nonce is a random
-string no longer than 32 bytes.
+calls $self->UserObj->HasRight with the arguments passed in
=cut
-sub Authenticate {
- my ($self, $password, $created, $nonce, $realm) = @_;
+sub HasRight {
+ my $self = shift;
+ return ($self->UserObj->HasRight(@_));
+}
+
+# }}}
+
+# {{{ Localization
+
+=head2 LanguageHandle
+
+Returns this current user's langauge handle. Should take a language
+specification. but currently doesn't
- require Digest::MD5;
- require Digest::SHA1;
- require MIME::Base64;
+=begin testing
+
+ok (my $cu = RT::CurrentUser->new('root'));
+ok (my $lh = $cu->LanguageHandle);
+ok ($lh != undef);
+ok ($lh->isa('Locale::Maketext'));
+ok ($cu->loc('TEST_STRING') eq "Concrete Mixer", "Localized TEST_STRING into English");
+ok ($lh = $cu->LanguageHandle('fr'));
+ok ($cu->loc('Before') eq "Avant", "Localized TEST_STRING into Frenc");
+
+=end testing
+
+=cut
- my $username = $self->UserObj->Name or return;
- my $server_pass = $self->UserObj->__Value('Password') or return;
- my $auth_digest = MIME::Base64::encode_base64(Digest::SHA1::sha1(
- $nonce .
- $created .
- Digest::MD5::md5_hex("$username:$realm:$server_pass")
- ));
+sub LanguageHandle {
+ my $self = shift;
+ if ((!defined $self->{'LangHandle'}) ||
+ (!UNIVERSAL::can($self->{'LangHandle'}, 'maketext')) ||
+ (@_)) {
+ $self->{'LangHandle'} = RT::I18N->get_handle(@_);
+ }
+ # Fall back to english.
+ unless ($self->{'LangHandle'}) {
+ die "We couldn't get a dictionary. Nye mogu naidti slovar. No puedo encontrar dictionario.";
+ }
+ return ($self->{'LangHandle'});
+}
- chomp($password);
- chomp($auth_digest);
+sub loc {
+ my $self = shift;
+ return '' if $_[0] eq '';
+
+ my $handle = $self->LanguageHandle;
+
+ if (@_ == 1) {
+ # pre-scan the lexicon hashes to return _AUTO keys verbatim,
+ # to keep locstrings containing '[' and '~' from tripping over Maketext
+ return $_[0] unless grep { exists $_->{$_[0]} } @{ $handle->_lex_refs };
+ }
+
+ return $handle->maketext(@_);
+}
+
+sub loc_fuzzy {
+ my $self = shift;
+ return '' if $_[0] eq '';
+
+ # XXX: work around perl's deficiency when matching utf8 data
+ return $_[0] if Encode::is_utf8($_[0]);
+ my $result = $self->LanguageHandle->maketext_fuzzy(@_);
- return ($password eq $auth_digest);
+ return($result);
}
+# }}}
eval "require RT::CurrentUser_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/CurrentUser_Vendor.pm});
@@ -317,3 +371,4 @@ eval "require RT::CurrentUser_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/CurrentUser_Local.pm});
1;
+
diff --git a/rt/lib/RT/Date.pm b/rt/lib/RT/Date.pm
index fc4c43ce4..355370ada 100644
--- a/rt/lib/RT/Date.pm
+++ b/rt/lib/RT/Date.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
=head1 NAME
RT::Date - a simple Object Oriented date.
@@ -60,6 +35,11 @@ RT Date is a simple Date Object designed to be speedy and easy for RT to use
The fact that it assumes that a time of 0 means "never" is probably a bug.
+=begin testing
+
+ok (require RT::Date);
+
+=end testing
=head1 METHODS
@@ -69,11 +49,12 @@ The fact that it assumes that a time of 0 means "never" is probably a bug.
package RT::Date;
use Time::Local;
-use POSIX qw(tzset);
+
+use RT::Base;
use strict;
-use warnings;
-use base qw/RT::Base/;
+use vars qw/@ISA/;
+@ISA = qw/RT::Base/;
use vars qw($MINUTE $HOUR $DAY $WEEK $MONTH $YEAR);
@@ -81,68 +62,30 @@ $MINUTE = 60;
$HOUR = 60 * $MINUTE;
$DAY = 24 * $HOUR;
$WEEK = 7 * $DAY;
-$MONTH = 30.4375 * $DAY;
-$YEAR = 365.25 * $DAY;
-
-our @MONTHS = (
- 'Jan', # loc
- 'Feb', # loc
- 'Mar', # loc
- 'Apr', # loc
- 'May', # loc
- 'Jun', # loc
- 'Jul', # loc
- 'Aug', # loc
- 'Sep', # loc
- 'Oct', # loc
- 'Nov', # loc
- 'Dec', # loc
-);
-
-our @DAYS_OF_WEEK = (
- 'Sun', # loc
- 'Mon', # loc
- 'Tue', # loc
- 'Wed', # loc
- 'Thu', # loc
- 'Fri', # loc
- 'Sat', # loc
-);
-
-our @FORMATTERS = (
- 'DefaultFormat', # loc
- 'ISO', # loc
- 'W3CDTF', # loc
- 'RFC2822', # loc
- 'RFC2616', # loc
- 'iCal', # loc
-);
-if ( eval 'use DateTime qw(); 1;' && eval 'use DateTime::Locale qw(); 1;' &&
- DateTime->can('format_cldr') && DateTime::Locale::root->can('date_format_full') ) {
- push @FORMATTERS, 'LocalizedDateTime'; # loc
+$MONTH = 4 * $WEEK;
+$YEAR = 365 * $DAY;
+
+# {{{ sub new
+
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ bless ($self, $class);
+ $self->CurrentUser(@_);
+ $self->Unix(0);
+ return $self;
}
-=head2 new
-
-Object constructor takes one argument C<RT::CurrentUser> object.
-
-=cut
+# }}}
-sub new {
- my $proto = shift;
- my $class = ref($proto) || $proto;
- my $self = {};
- bless ($self, $class);
- $self->CurrentUser(@_);
- $self->Unix(0);
- return $self;
-}
+# {{{ sub Set
-=head2 Set
+=head2 sub Set
-Takes a param hash with the fields C<Format>, C<Value> and C<Timezone>.
+takes a param hash with the fields 'Format' and 'Value'
-If $args->{'Format'} is 'unix', takes the number of seconds since the epoch.
+if $args->{'Format'} is 'unix', takes the number of seconds since the epoch
If $args->{'Format'} is ISO, tries to parse an ISO date.
@@ -151,218 +94,243 @@ things out. This is a heavyweight operation that should never be called from
within RT's core. But it's really useful for something like the textbox date
entry where we let the user do whatever they want.
-If $args->{'Value'} is 0, assumes you mean never.
+If $args->{'Value'} is 0, assumes you mean never.
+
+=begin testing
+
+use_ok(RT::Date);
+my $date = RT::Date->new($RT::SystemUser);
+$date->Set(Format => 'unix', Value => '0');
+ok ($date->ISO eq '1970-01-01 00:00:00', "Set a date to midnight 1/1/1970 GMT");
+
+=end testing
=cut
sub Set {
my $self = shift;
- my %args = (
- Format => 'unix',
- Value => time,
- Timezone => 'user',
- @_
- );
-
- return $self->Unix(0) unless $args{'Value'};
+ my %args = ( Format => 'unix',
+ Value => time,
+ @_ );
+ if ( !$args{'Value'}
+ || ( ( $args{'Value'} =~ /^\d*$/ ) and ( $args{'Value'} == 0 ) ) ) {
+ $self->Unix(-1);
+ return ( $self->Unix() );
+ }
if ( $args{'Format'} =~ /^unix$/i ) {
- return $self->Unix( $args{'Value'} );
+ $self->Unix( $args{'Value'} );
}
- elsif ( $args{'Format'} =~ /^(sql|datemanip|iso)$/i ) {
- $args{'Value'} =~ s!/!-!g;
- if ( ( $args{'Value'} =~ /^(\d{4})?(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ )
- || ( $args{'Value'} =~ /^(\d{4})?(\d\d)(\d\d)(\d\d):(\d\d):(\d\d)$/ )
- || ( $args{'Value'} =~ /^(?:(\d{4})-)?(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ )
- || ( $args{'Value'} =~ /^(?:(\d{4})-)?(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)\+00$/ )
+ elsif ( $args{'Format'} =~ /^(sql|datemanip|iso)$/i ) {
+ $args{'Value'} =~ s!/!-!g;
+
+ if (( $args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/ )
+ || ( $args{'Value'} =~
+ /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$/ )
+ || ( $args{'Value'} =~
+ /^(\d{4}?)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)\+00$/ )
+ || ($args{'Value'} =~ /^(\d{4}?)(\d\d)(\d\d)(\d\d):(\d\d):(\d\d)$/ )
) {
- my ($year, $mon, $mday, $hours, $min, $sec) = ($1, $2, $3, $4, $5, $6);
-
- # use current year if string has no value
- $year ||= (localtime time)[5] + 1900;
+ my $year = $1;
+ my $mon = $2;
+ my $mday = $3;
+ my $hours = $4;
+ my $min = $5;
+ my $sec = $6;
#timegm expects month as 0->11
$mon--;
- #now that we've parsed it, deal with the case where everything was 0
- return $self->Unix(0) if $mon < 0 || $mon > 11;
-
- my $tz = lc $args{'Format'} eq 'datemanip'? 'user': 'utc';
- $self->Unix( $self->Timelocal( $tz, $sec, $min, $hours, $mday, $mon, $year ) );
-
- $self->Unix(0) unless $self->Unix > 0;
+ #now that we've parsed it, deal with the case where everything
+ #was 0
+ if ( $mon == -1 ) {
+ $self->Unix(-1);
+ }
+ else {
+
+ #Dateamnip strings aren't in GMT.
+ if ( $args{'Format'} =~ /^datemanip$/i ) {
+ $self->Unix(
+ timelocal( $sec, $min, $hours, $mday, $mon, $year ) );
+ }
+
+ #ISO and SQL dates are in GMT
+ else {
+ $self->Unix(
+ timegm( $sec, $min, $hours, $mday, $mon, $year ) );
+ }
+
+ $self->Unix(-1) unless $self->Unix;
+ }
}
else {
- $RT::Logger->warning(
- "Couldn't parse date '$args{'Value'}' as a $args{'Format'} format"
- );
- return $self->Unix(0);
+ use Carp;
+ Carp::cluck;
+ $RT::Logger->debug(
+ "Couldn't parse date $args{'Value'} as a $args{'Format'}");
+
}
}
elsif ( $args{'Format'} =~ /^unknown$/i ) {
require Time::ParseDate;
- # the module supports only legacy timezones like PDT or EST...
- # so we parse date as GMT and later apply offset, this only
- # should be applied to absolute times, so compensate shift in NOW
- my $now = time;
- $now += ($self->Localtime( $args{Timezone}, $now ))[9];
- my $date = Time::ParseDate::parsedate(
- $args{'Value'},
- GMT => 1,
- NOW => $now,
- UK => RT->Config->Get('DateDayBeforeMonth'),
- PREFER_PAST => RT->Config->Get('AmbiguousDayInPast'),
- PREFER_FUTURE => RT->Config->Get('AmbiguousDayInFuture'),
- );
- # apply timezone offset
- $date -= ($self->Localtime( $args{Timezone}, $date ))[9];
-
- $RT::Logger->debug(
- "RT::Date used Time::ParseDate to make '$args{'Value'}' $date\n"
- );
-
- return $self->Set( Format => 'unix', Value => $date);
- }
- else {
- $RT::Logger->error(
- "Unknown Date format: $args{'Format'}\n"
- );
- return $self->Unix(0);
- }
-
- return $self->Unix;
-}
-
-=head2 SetToNow
-Set the object's time to the current time. Takes no arguments
-and returns unix time.
+ #Convert it to an ISO format string
-=cut
+ my $date = Time::ParseDate::parsedate($args{'Value'},
+ UK => $RT::DateDayBeforeMonth,
+ PREFER_PAST => $RT::AmbiguousDayInPast,
+ PREFER_FUTURE => !($RT::AmbiguousDayInPast));
-sub SetToNow {
- return $_[0]->Unix(time);
-}
+ #This date has now been set to a date in the _local_ timezone.
+ #since ISO dates are known to be in GMT (for RT's purposes);
-=head2 SetToMidnight [Timezone => 'utc']
+ $RT::Logger->debug( "RT::Date used date::parse to make "
+ . $args{'Value'}
+ . " $date\n" );
-Sets the date to midnight (at the beginning of the day).
-Returns the unixtime at midnight.
+ return ( $self->Set( Format => 'unix', Value => "$date" ) );
+ }
+ else {
+ die "Unknown Date format: " . $args{'Format'} . "\n";
+ }
-Arguments:
+ return ( $self->Unix() );
+}
-=over 4
+# }}}
-=item Timezone
+# {{{ sub SetToMidnight
-Timezone context C<user>, C<server> or C<UTC>. See also L</Timezone>.
+=head2 SetToMidnight
-=back
+Sets the date to midnight (at the beginning of the day) GMT
+Returns the unixtime at midnight.
=cut
sub SetToMidnight {
my $self = shift;
- my %args = ( Timezone => '', @_ );
- my $new = $self->Timelocal(
- $args{'Timezone'},
- 0,0,0,($self->Localtime( $args{'Timezone'} ))[3..9]
- );
- return $self->Unix( $new );
+
+ use Time::Local;
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($self->Unix);
+ $self->Unix(timegm (0,0,0,$mday,$mon,$year,$wday,$yday));
+
+ return ($self->Unix);
+
+
}
+
+# }}}
+
+# {{{ sub SetToNow
+sub SetToNow {
+ my $self = shift;
+ return($self->Set(Format => 'unix', Value => time))
+}
+# }}}
+
+# {{{ sub Diff
+
=head2 Diff
-Takes either an C<RT::Date> object or the date in unixtime format as a string,
-if nothing is specified uses the current time.
+Takes either an RT::Date object or the date in unixtime format as a string
-Returns the differnce between the time in the current object and that time
-as a number of seconds. Returns C<undef> if any of two compared values is
-incorrect or not set.
+Returns the differnce between $self and that time as a number of seconds
=cut
sub Diff {
my $self = shift;
my $other = shift;
- $other = time unless defined $other;
- if ( UNIVERSAL::isa( $other, 'RT::Date' ) ) {
- $other = $other->Unix;
- }
- return undef unless $other=~ /^\d+$/ && $other > 0;
-
- my $unix = $self->Unix;
- return undef unless $unix > 0;
- return $unix - $other;
+ if (ref($other) eq 'RT::Date') {
+ $other=$other->Unix;
+ }
+ return ($self->Unix - $other);
}
+# }}}
+
+# {{{ sub DiffAsString
-=head2 DiffAsString
+=head2 sub DiffAsString
-Takes either an C<RT::Date> object or the date in unixtime format as a string,
-if nothing is specified uses the current time.
+Takes either an RT::Date object or the date in unixtime format as a string
-Returns the differnce between C<$self> and that time as a number of seconds as
-a localized string fit for human consumption. Returns empty string if any of
-two compared values is incorrect or not set.
+Returns the differnce between $self and that time as a number of seconds as
+as string fit for human consumption
=cut
sub DiffAsString {
my $self = shift;
- my $diff = $self->Diff( @_ );
- return '' unless defined $diff;
+ my $other = shift;
+
- return $self->DurationAsString( $diff );
+ if ($other < 1) {
+ return ("");
+ }
+ if ($self->Unix < 1) {
+ return("");
+ }
+ my $diff = $self->Diff($other);
+
+ return ($self->DurationAsString($diff));
}
+# }}}
+
+# {{{ sub DurationAsString
+
=head2 DurationAsString
-Takes a number of seconds. Returns a localized string describing
-that duration.
+Takes a number of seconds. returns a string describing that duration
=cut
sub DurationAsString {
+
my $self = shift;
- my $duration = int shift;
+ my $duration = shift;
+
+ my ( $negative, $s );
- my ( $negative, $s, $time_unit );
- $negative = 1 if $duration < 0;
- $duration = abs $duration;
+ $negative = 1 if ( $duration < 0 );
+ $duration = abs($duration);
+
+ my $time_unit;
if ( $duration < $MINUTE ) {
$s = $duration;
$time_unit = $self->loc("sec");
}
elsif ( $duration < ( 2 * $HOUR ) ) {
- $s = int( $duration / $MINUTE + 0.5 );
+ $s = int( $duration / $MINUTE );
$time_unit = $self->loc("min");
}
elsif ( $duration < ( 2 * $DAY ) ) {
- $s = int( $duration / $HOUR + 0.5 );
+ $s = int( $duration / $HOUR );
$time_unit = $self->loc("hours");
}
elsif ( $duration < ( 2 * $WEEK ) ) {
- $s = int( $duration / $DAY + 0.5 );
+ $s = int( $duration / $DAY );
$time_unit = $self->loc("days");
}
elsif ( $duration < ( 2 * $MONTH ) ) {
- $s = int( $duration / $WEEK + 0.5 );
+ $s = int( $duration / $WEEK );
$time_unit = $self->loc("weeks");
}
elsif ( $duration < $YEAR ) {
- $s = int( $duration / $MONTH + 0.5 );
+ $s = int( $duration / $MONTH );
$time_unit = $self->loc("months");
}
else {
- $s = int( $duration / $YEAR + 0.5 );
+ $s = int( $duration / $YEAR );
$time_unit = $self->loc("years");
}
-
- if ( $negative ) {
+ if (0) { # For now, never display the "AGO" # $negative) {
return $self->loc( "[_1] [_2] ago", $s, $time_unit );
}
else {
@@ -370,46 +338,46 @@ sub DurationAsString {
}
}
-=head2 AgeAsString
+# }}}
-Takes nothing. Returns a string that's the differnce between the
-time in the object and now.
+# {{{ sub AgeAsString
-=cut
+=head2 sub AgeAsString
+
+Takes nothing
-sub AgeAsString { return $_[0]->DiffAsString }
+Returns a string that's the differnce between the time in the object and now
+=cut
+sub AgeAsString {
+ my $self = shift;
+ return ($self->DiffAsString(time));
+ }
+# }}}
-=head2 AsString
+# {{{ sub AsString
-Returns the object's time as a localized string with curent user's prefered
-format and timezone.
+=head2 sub AsString
-If the current user didn't choose prefered format then system wide setting is
-used or L</DefaultFormat> if the latter is not specified. See config option
-C<DateTimeFormat>.
+Returns the object\'s time as a string with the current timezone.
=cut
sub AsString {
my $self = shift;
- my %args = (@_);
-
- return $self->loc("Not set") unless $self->Unix > 0;
+ return ($self->loc("Not set")) if ($self->Unix <= 0);
- my $format = RT->Config->Get( 'DateTimeFormat', $self->CurrentUser ) || 'DefaultFormat';
- $format = { Format => $format } unless ref $format;
- %args = (%$format, %args);
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->Unix);
- return $self->Get( Timezone => 'user', %args );
+ return $self->loc("[_1] [_2] [_3] [_4]:[_5]:[_6] [_7]", $self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900));
}
+# }}}
+# {{{ GetWeekday
=head2 GetWeekday DAY
-Takes an integer day of week and returns a localized string for
-that day of week. Valid values are from range 0-6, Note that B<0
-is sunday>.
+Takes an integer day of week and returns a localized string for that day of week
=cut
@@ -417,628 +385,169 @@ sub GetWeekday {
my $self = shift;
my $dow = shift;
- return $self->loc($DAYS_OF_WEEK[$dow])
- if $DAYS_OF_WEEK[$dow];
- return '';
+ return $self->loc('Mon.') if ($dow == 1);
+ return $self->loc('Tue.') if ($dow == 2);
+ return $self->loc('Wed.') if ($dow == 3);
+ return $self->loc('Thu.') if ($dow == 4);
+ return $self->loc('Fri.') if ($dow == 5);
+ return $self->loc('Sat.') if ($dow == 6);
+ return $self->loc('Sun.') if ($dow == 0);
}
-=head2 GetMonth MONTH
+# }}}
-Takes an integer month and returns a localized string for that month.
-Valid values are from from range 0-11.
+# {{{ GetMonth
+=head2 GetMonth DAY
+
+Takes an integer month and returns a localized string for that month
=cut
sub GetMonth {
my $self = shift;
- my $mon = shift;
-
- return $self->loc($MONTHS[$mon])
- if $MONTHS[$mon];
- return '';
+ my $mon = shift;
+
+ # We do this rather than an array so that we don't call localize 12x what we need to
+ return $self->loc('Jan.') if ($mon == 0);
+ return $self->loc('Feb.') if ($mon == 1);
+ return $self->loc('Mar.') if ($mon == 2);
+ return $self->loc('Apr.') if ($mon == 3);
+ return $self->loc('May.') if ($mon == 4);
+ return $self->loc('Jun.') if ($mon == 5);
+ return $self->loc('Jul.') if ($mon == 6);
+ return $self->loc('Aug.') if ($mon == 7);
+ return $self->loc('Sep.') if ($mon == 8);
+ return $self->loc('Oct.') if ($mon == 9);
+ return $self->loc('Nov.') if ($mon == 10);
+ return $self->loc('Dec.') if ($mon == 11);
}
-=head2 AddSeconds SECONDS
+# }}}
-Takes a number of seconds and returns the new unix time.
+# {{{ sub AddSeconds
-Negative value can be used to substract seconds.
+=head2 sub AddSeconds
+
+Takes a number of seconds as a string
+
+Returns the new time
=cut
sub AddSeconds {
my $self = shift;
- my $delta = shift or return $self->Unix;
+ my $delta = shift;
$self->Set(Format => 'unix', Value => ($self->Unix + $delta));
-
+
return ($self->Unix);
-}
-
-=head2 AddDays [DAYS]
-
-Adds C<24 hours * DAYS> to the current time. Adds one day when
-no argument is specified. Negative value can be used to substract
-days.
-
-Returns new unix time.
-
-=cut
-
-sub AddDays {
- my $self = shift;
- my $days = shift || 1;
- return $self->AddSeconds( $days * $DAY );
-}
-
-=head2 AddDay
-
-Adds 24 hours to the current time. Returns new unix time.
-
-=cut
-
-sub AddDay { return $_[0]->AddSeconds($DAY) }
-
-=head2 Unix [unixtime]
-
-Optionally takes a date in unix seconds since the epoch format.
-Returns the number of seconds since the epoch
-
-=cut
-
-sub Unix {
- my $self = shift;
- $self->{'time'} = int(shift || 0) if @_;
- return $self->{'time'};
-}
-
-=head2 DateTime
-
-Alias for L</Get> method. Arguments C<Date> and <Time>
-are fixed to true values, other arguments could be used
-as described in L</Get>.
-
-=cut
-
-sub DateTime {
- my $self = shift;
- unless (defined $self) {
- use Carp; Carp::confess("undefined $self");
- }
- return $self->Get( @_, Date => 1, Time => 1 );
-}
-
-=head2 Date
-
-Takes Format argument which allows you choose date formatter.
-Pass throught other arguments to the formatter method.
-
-Returns the object's formatted date. Default formatter is ISO.
-
-=cut
-
-sub Date {
- my $self = shift;
- return $self->Get( @_, Date => 1, Time => 0 );
-}
-
-=head2 Time
-
-
-=cut
-
-sub Time {
- my $self = shift;
- return $self->Get( @_, Date => 0, Time => 1 );
-}
-
-=head2 Get
-
-Returnsa a formatted and localized string that represets time of
-the current object.
-
-
-=cut
+
-sub Get
-{
- my $self = shift;
- my %args = (Format => 'ISO', @_);
- my $formatter = $args{'Format'};
- $formatter = 'ISO' unless $self->can($formatter);
- return $self->$formatter( %args );
}
-=head2 Output formatters
-
-Fomatter is a method that returns date and time in different configurable
-format.
-
-Each method takes several arguments:
-
-=over 1
-
-=item Date
-
-=item Time
-
-=item Timezone - Timezone context C<server>, C<user> or C<UTC>
-
-=back
+# }}}
-Formatters may also add own arguments to the list, for example
-in RFC2822 format day of time in output is optional so it
-understand boolean argument C<DayOfTime>.
+# {{{ sub AddDays
-=head3 Formatters
+=head2 AddDays $DAYS
-Returns an array of available formatters.
+Adds 24 hours * $DAYS to the current time
=cut
-sub Formatters
-{
- my $self = shift;
-
- return @FORMATTERS;
-}
-
-=head3 DefaultFormat
-
-=cut
-
-sub DefaultFormat
-{
+sub AddDays {
my $self = shift;
- my %args = ( Date => 1,
- Time => 1,
- Timezone => '',
- Seconds => 1,
- @_,
- );
+ my $days = shift;
+ $self->AddSeconds($days * $DAY);
- # 0 1 2 3 4 5 6 7 8 9
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime($args{'Timezone'});
- $wday = $self->GetWeekday($wday);
- $mon = $self->GetMonth($mon);
- ($mday, $hour, $min, $sec) = map { sprintf "%02d", $_ } ($mday, $hour, $min, $sec);
-
- if( $args{'Date'} && !$args{'Time'} ) {
- return $self->loc('[_1] [_2] [_3] [_4]',
- $wday,$mon,$mday,$year);
- } elsif( !$args{'Date'} && $args{'Time'} ) {
- if( $args{'Seconds'} ) {
- return $self->loc('[_1]:[_2]:[_3]',
- $hour,$min,$sec);
- } else {
- return $self->loc('[_1]:[_2]',
- $hour,$min);
- }
- } else {
- if( $args{'Seconds'} ) {
- return $self->loc('[_1] [_2] [_3] [_4]:[_5]:[_6] [_7]',
- $wday,$mon,$mday,$hour,$min,$sec,$year);
- } else {
- return $self->loc('[_1] [_2] [_3] [_4]:[_5] [_6]',
- $wday,$mon,$mday,$hour,$min,$year);
- }
- }
}
-=head3 LocalizedDateTime
+# }}}
-Returns date and time as string, with user localization.
+# {{{ sub AddDay
-Supports arguments: C<DateFormat> and C<TimeFormat> which may contains date and
-time format as specified in DateTime::Locale (default to full_date_format and
-medium_time_format), C<AbbrDay> and C<AbbrMonth> which may be set to 0 if
-you want full Day/Month names instead of abbreviated ones.
+=head2 AddDay
-Require optionnal DateTime::Locale module.
+Adds 24 hours to the current time
=cut
-sub LocalizedDateTime
-{
+sub AddDay {
my $self = shift;
- my %args = ( Date => 1,
- Time => 1,
- Timezone => '',
- DateFormat => 'date_format_full',
- TimeFormat => 'time_format_medium',
- AbbrDay => 1,
- AbbrMonth => 1,
- @_,
- );
-
- return $self->loc("DateTime module missing") unless ( eval 'use DateTime qw(); 1;' );
- return $self->loc("DateTime::Locale module missing") unless ( eval 'use DateTime::Locale qw(); 1;' );
- return $self->loc("DateTime doesn't support format_cldr, you must upgrade to use this feature")
- unless can DateTime::('format_cldr');
-
-
- my $date_format = $args{'DateFormat'};
- my $time_format = $args{'TimeFormat'};
-
- my $lang = $self->CurrentUser->UserObj->Lang;
- unless ($lang) {
- require I18N::LangTags::Detect;
- $lang = ( I18N::LangTags::Detect::detect(), 'en' )[0];
- }
+ $self->AddSeconds($DAY);
-
- my $formatter = DateTime::Locale->load($lang);
- return $self->loc("DateTime::Locale doesn't support date_format_full, you must upgrade to use this feature")
- unless $formatter->can('date_format_full');
- $date_format = $formatter->$date_format;
- $time_format = $formatter->$time_format;
- $date_format =~ s/EEEE/EEE/g if ( $args{'AbbrDay'} );
- $date_format =~ s/MMMM/MMM/g if ( $args{'AbbrMonth'} );
-
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime($args{'Timezone'});
- $mon++;
- my $tz = $self->Timezone($args{'Timezone'});
-
- # FIXME : another way to call this module without conflict with local
- # DateTime method?
- my $dt = new DateTime::( locale => $lang,
- time_zone => $tz,
- year => $year,
- month => $mon,
- day => $mday,
- hour => $hour,
- minute => $min,
- second => $sec,
- nanosecond => 0,
- );
-
- if ( $args{'Date'} && !$args{'Time'} ) {
- return $dt->format_cldr($date_format);
- } elsif ( !$args{'Date'} && $args{'Time'} ) {
- return $dt->format_cldr($time_format);
- } else {
- return $dt->format_cldr($date_format) . " " . $dt->format_cldr($time_format);
- }
-}
-
-=head3 ISO
-
-Returns the object's date in ISO format C<YYYY-MM-DD mm:hh:ss>.
-ISO format is locale independant, but adding timezone offset info
-is not implemented yet.
-
-Supports arguments: C<Timezone>, C<Date>, C<Time> and C<Seconds>.
-See </Output formatters> for description of arguments.
-
-=cut
-
-sub ISO {
- my $self = shift;
- my %args = ( Date => 1,
- Time => 1,
- Timezone => '',
- Seconds => 1,
- @_,
- );
- # 0 1 2 3 4 5 6 7 8 9
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime($args{'Timezone'});
-
- #the month needs incrementing, as gmtime returns 0-11
- $mon++;
-
- my $res = '';
- $res .= sprintf("%04d-%02d-%02d", $year, $mon, $mday) if $args{'Date'};
- $res .= sprintf(' %02d:%02d', $hour, $min) if $args{'Time'};
- $res .= sprintf(':%02d', $sec, $min) if $args{'Time'} && $args{'Seconds'};
- $res =~ s/^\s+//;
-
- return $res;
}
-=head3 W3CDTF
-
-Returns the object's date and time in W3C date time format
-(L<http://www.w3.org/TR/NOTE-datetime>).
-
-Format is locale independand and is close enought to ISO, but
-note that date part is B<not optional> and output string
-has timezone offset mark in C<[+-]hh:mm> format.
-
-Supports arguments: C<Timezone>, C<Time> and C<Seconds>.
-See </Output formatters> for description of arguments.
-
-=cut
-
-sub W3CDTF {
- my $self = shift;
- my %args = (
- Time => 1,
- Timezone => '',
- Seconds => 1,
- @_,
- Date => 1,
- );
- # 0 1 2 3 4 5 6 7 8 9
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime( $args{'Timezone'} );
-
- #the month needs incrementing, as gmtime returns 0-11
- $mon++;
-
- my $res = '';
- $res .= sprintf("%04d-%02d-%02d", $year, $mon, $mday);
- if ( $args{'Time'} ) {
- $res .= sprintf('T%02d:%02d', $hour, $min);
- $res .= sprintf(':%02d', $sec, $min) if $args{'Seconds'};
- if ( $offset ) {
- $res .= sprintf "%s%02d:%02d", $self->_SplitOffset( $offset );
- } else {
- $res .= 'Z';
- }
- }
-
- return $res;
-};
+# }}}
+# {{{ sub Unix
-=head3 RFC2822 (MIME)
+=head2 sub Unix [unixtime]
-Returns the object's date and time in RFC2822 format,
-for example C<Sun, 06 Nov 1994 08:49:37 +0000>.
-Format is locale independand as required by RFC. Time
-part always has timezone offset in digits with sign prefix.
-
-Supports arguments: C<Timezone>, C<Date>, C<Time>, C<DayOfWeek>
-and C<Seconds>. See </Output formatters> for description of
-arguments.
+Optionally takes a date in unix seconds since the epoch format.
+Returns the number of seconds since the epoch
=cut
-sub RFC2822 {
+sub Unix {
my $self = shift;
- my %args = ( Date => 1,
- Time => 1,
- Timezone => '',
- DayOfWeek => 1,
- Seconds => 1,
- @_,
- );
-
- # 0 1 2 3 4 5 6 7 8 9
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime($args{'Timezone'});
-
- my ($date, $time) = ('','');
- $date .= "$DAYS_OF_WEEK[$wday], " if $args{'DayOfWeek'} && $args{'Date'};
- $date .= "$mday $MONTHS[$mon] $year" if $args{'Date'};
-
- if ( $args{'Time'} ) {
- $time .= sprintf("%02d:%02d", $hour, $min);
- $time .= sprintf(":%02d", $sec) if $args{'Seconds'};
- $time .= sprintf " %s%02d%02d", $self->_SplitOffset( $offset );
- }
-
- return join ' ', grep $_, ($date, $time);
+
+ $self->{'time'} = shift if (@_);
+
+ return ($self->{'time'});
}
+# }}}
-=head3 RFC2616 (HTTP)
+# {{{ sub ISO
-Returns the object's date and time in RFC2616 (HTTP/1.1) format,
-for example C<Sun, 06 Nov 1994 08:49:37 GMT>. While the RFC describes
-version 1.1 of HTTP, but the same form date can be used in version 1.0.
+=head2 ISO
-Format is fixed length, locale independand and always represented in GMT
-what makes it quite useless for users, but any date in HTTP transfers
-must be presented using this format.
+Takes nothing
- HTTP-date = rfc1123 | ...
- rfc1123 = wkday "," SP date SP time SP "GMT"
- date = 2DIGIT SP month SP 4DIGIT
- ; day month year (e.g., 02 Jun 1982)
- time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
- ; 00:00:00 - 23:59:59
- wkday = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun"
- month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun"
- | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
-
-Supports arguments: C<Date> and C<Time>, but you should use them only for
-some personal reasons, RFC2616 doesn't define any optional parts.
-See </Output formatters> for description of arguments.
+Returns the object's date in ISO format
=cut
-sub RFC2616 {
- my $self = shift;
- my %args = ( Date => 1, Time => 1,
- @_,
- Timezone => 'utc',
- Seconds => 1, DayOfWeek => 1,
- );
-
- my $res = $self->RFC2822( @_ );
- $res =~ s/\s*[+-]\d\d\d\d$/ GMT/ if $args{'Time'};
- return $res;
-}
-
-=head4 iCal
-
-Returns the object's date and time in iCalendar format,
-
-Supports arguments: C<Date> and C<Time>.
-See </Output formatters> for description of arguments.
-
-=cut
+sub ISO {
+ my $self=shift;
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst, $date) ;
+
+ return ('1970-01-01 00:00:00') if ($self->Unix == -1);
-sub iCal {
- my $self = shift;
- my %args = (
- Date => 1, Time => 1,
- @_,
- );
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$ydaym,$isdst,$offset) =
- $self->Localtime( 'utc' );
+ # 0 1 2 3 4 5 6 7 8
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($self->Unix);
+ #make the year YYYY
+ $year+=1900;
#the month needs incrementing, as gmtime returns 0-11
$mon++;
-
- my $res;
- if ( $args{'Date'} && !$args{'Time'} ) {
- $res = sprintf( '%04d%02d%02d', $year, $mon, $mday );
- }
- elsif ( !$args{'Date'} && $args{'Time'} ) {
- $res = sprintf( 'T%02d%02d%02dZ', $hour, $min, $sec );
- }
- else {
- $res = sprintf( '%04d%02d%02dT%02d%02d%02dZ', $year, $mon, $mday, $hour, $min, $sec );
- }
- return $res;
-}
-
-# it's been added by mistake in 3.8.0
-sub iCalDate { return (shift)->iCal( Time => 0, @_ ) }
-
-sub _SplitOffset {
- my ($self, $offset) = @_;
- my $sign = $offset < 0? '-': '+';
- $offset = int( (abs $offset) / 60 + 0.001 );
- my $mins = $offset % 60;
- my $hours = int( $offset/60 + 0.001 );
- return $sign, $hours, $mins;
-}
-
-=head2 Timezones handling
-
-=head3 Localtime $context [$time]
-
-Takes one mandatory argument C<$context>, which determines whether
-we want "user local", "system" or "UTC" time. Also, takes optional
-argument unix C<$time>, default value is the current unix time.
-
-Returns object's date and time in the format provided by perl's
-builtin functions C<localtime> and C<gmtime> with two exceptions:
-
-1) "Year" is a four-digit year, rather than "years since 1900"
-
-2) The last element of the array returned is C<offset>, which
-represents timezone offset against C<UTC> in seconds.
-
-=cut
-
-sub Localtime
-{
- my $self = shift;
- my $tz = $self->Timezone(shift);
-
- my $unix = shift || $self->Unix;
- $unix = 0 unless $unix >= 0;
+
+ $date = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $year,$mon,$mday, $hour,$min,$sec);
- my @local;
- if ($tz eq 'UTC') {
- @local = gmtime($unix);
- } else {
- {
- local $ENV{'TZ'} = $tz;
- ## Using POSIX::tzset fixes a bug where the TZ environment variable
- ## is cached.
- POSIX::tzset();
- @local = localtime($unix);
- }
- POSIX::tzset(); # return back previouse value
- }
- $local[5] += 1900; # change year to 4+ digits format
- my $offset = Time::Local::timegm_nocheck(@local) - $unix;
- return @local, $offset;
-}
-
-=head3 Timelocal $context @time
-
-Takes argument C<$context>, which determines whether we should
-treat C<@time> as "user local", "system" or "UTC" time.
-
-C<@time> is array returned by L<Localtime> functions. Only first
-six elements are mandatory - $sec, $min, $hour, $mday, $mon and $year.
-You may pass $wday, $yday and $isdst, these are ignored.
-
-If you pass C<$offset> as ninth argument, it's used instead of
-C<$context>. It's done such way as code
-C<$self->Timelocal('utc', $self->Localtime('server'))> doesn't
-makes much sense and most probably would produce unexpected
-result, so the method ignore 'utc' context and uses offset
-returned by L<Localtime> method.
-
-=cut
-
-sub Timelocal {
- my $self = shift;
- my $tz = shift;
- if ( defined $_[9] ) {
- return timegm(@_[0..5]) - $_[9];
- } else {
- $tz = $self->Timezone( $tz );
- if ( $tz eq 'UTC' ) {
- return Time::Local::timegm(@_[0..5]);
- } else {
- my $rv;
- {
- local $ENV{'TZ'} = $tz;
- ## Using POSIX::tzset fixes a bug where the TZ environment variable
- ## is cached.
- POSIX::tzset();
- $rv = Time::Local::timelocal(@_[0..5]);
- };
- POSIX::tzset(); # switch back to previouse value
- return $rv;
- }
- }
+ return ($date);
}
+# }}}
-=head3 Timezone $context
-
-Returns the timezone name.
-
-Takes one argument, C<$context> argument which could be C<user>, C<server> or C<utc>.
-=over
+# {{{ sub LocalTimezone
+=head2 LocalTimezone
-=item user
-
-Default value is C<user> that mean it returns current user's Timezone value.
-
-=item server
-
-If context is C<server> it returns value of the C<Timezone> RT config option.
-
-=item utc
-
-If both server's and user's timezone names are undefined returns 'UTC'.
-
-=back
+ Returns the current timezone. For now, draws off a system timezone, RT::Timezone. Eventually, this may
+pull from a 'Timezone' attribute of the CurrentUser
=cut
-sub Timezone {
+sub LocalTimezone {
my $self = shift;
- my $context = lc(shift);
- $context = 'utc' unless $context =~ /^(?:utc|server|user)$/i;
+ return $self->CurrentUser->Timezone
+ if $self->CurrentUser and $self->CurrentUser->can('Timezone');
- my $tz;
- if( $context eq 'user' ) {
- $tz = $self->CurrentUser->UserObj->Timezone;
- } elsif( $context eq 'server') {
- $tz = RT->Config->Get('Timezone');
- } else {
- $tz = 'UTC';
- }
- $tz ||= RT->Config->Get('Timezone') || 'UTC';
- $tz = 'UTC' if lc $tz eq 'gmt';
- return $tz;
+ return ($RT::Timezone);
}
+# }}}
eval "require RT::Date_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Date_Vendor.pm});
diff --git a/rt/lib/RT/Extension/ActivityReports.pm b/rt/lib/RT/Extension/ActivityReports.pm
new file mode 100644
index 000000000..52d8ba6ab
--- /dev/null
+++ b/rt/lib/RT/Extension/ActivityReports.pm
@@ -0,0 +1,3 @@
+package RT::Extension::ActivityReports;
+
+our $VERSION = '0.2';
diff --git a/rt/lib/RT/Group.pm b/rt/lib/RT/Group.pm
index 17444b06d..4dcef3f07 100755
--- a/rt/lib/RT/Group.pm
+++ b/rt/lib/RT/Group.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -94,7 +69,7 @@ Create takes a hash of values and creates a row in the database:
varchar(255) 'Description'.
varchar(64) 'Domain'.
varchar(64) 'Type'.
- int(11) 'Instance'.
+ varchar(64) 'Instance'.
=cut
@@ -123,7 +98,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -132,14 +107,14 @@ Returns the current value of id.
=cut
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -150,14 +125,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -168,14 +143,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Domain
+=item Domain
Returns the current value of Domain.
(In the database, Domain is stored as varchar(64).)
-=head2 SetDomain VALUE
+=item SetDomain VALUE
Set Domain to VALUE.
@@ -186,14 +161,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Type
+=item Type
Returns the current value of Type.
(In the database, Type is stored as varchar(64).)
-=head2 SetType VALUE
+=item SetType VALUE
Set Type to VALUE.
@@ -204,40 +179,40 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Instance
+=item Instance
Returns the current value of Instance.
-(In the database, Instance is stored as int(11).)
+(In the database, Instance is stored as varchar(64).)
-=head2 SetInstance VALUE
+=item SetInstance VALUE
Set Instance to VALUE.
Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, Instance will be stored as a int(11).)
+(In the database, Instance will be stored as a varchar(64).)
=cut
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
Domain =>
- {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
+ {read => 1, write => 1, type => 'varchar(64)', default => ''},
Type =>
- {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
+ {read => 1, write => 1, type => 'varchar(64)', default => ''},
Instance =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, write => 1, type => 'varchar(64)', default => ''},
}
};
@@ -269,7 +244,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/GroupMember.pm b/rt/lib/RT/GroupMember.pm
index f7fa73e9e..8de1a73fe 100755
--- a/rt/lib/RT/GroupMember.pm
+++ b/rt/lib/RT/GroupMember.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -114,7 +89,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -123,14 +98,14 @@ Returns the current value of id.
=cut
-=head2 GroupId
+=item GroupId
Returns the current value of GroupId.
(In the database, GroupId is stored as int(11).)
-=head2 SetGroupId VALUE
+=item SetGroupId VALUE
Set GroupId to VALUE.
@@ -141,14 +116,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 MemberId
+=item MemberId
Returns the current value of MemberId.
(In the database, MemberId is stored as int(11).)
-=head2 SetMemberId VALUE
+=item SetMemberId VALUE
Set MemberId to VALUE.
@@ -160,15 +135,15 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
GroupId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
MemberId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
}
};
@@ -200,7 +175,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/GroupMembers.pm b/rt/lib/RT/GroupMembers.pm
index 0e7514d3a..31cb9536f 100755
--- a/rt/lib/RT/GroupMembers.pm
+++ b/rt/lib/RT/GroupMembers.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::GroupMember item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Groups.pm b/rt/lib/RT/Groups.pm
index f8dac135b..29f12a5a0 100755
--- a/rt/lib/RT/Groups.pm
+++ b/rt/lib/RT/Groups.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
+# 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.
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Group item
@@ -116,6 +91,8 @@ sub NewItem {
};
+
+
=head1 SEE ALSO
This class allows "overlay" methods to be placed
@@ -124,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Groups_Overlay.pm b/rt/lib/RT/Groups_Overlay.pm
index 8d09755da..aba5cc0b3 100644
--- a/rt/lib/RT/Groups_Overlay.pm
+++ b/rt/lib/RT/Groups_Overlay.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::Groups - a collection of RT::Group objects
@@ -53,7 +52,7 @@
=head1 SYNOPSIS
use RT::Groups;
- my $groups = RT::Groups->new($CurrentUser);
+ my $groups = $RT::Groups->new($CurrentUser);
$groups->UnLimit();
while (my $group = $groups->Next()) {
print $group->Id ." is a group id\n";
@@ -65,6 +64,11 @@
=head1 METHODS
+=begin testing
+
+ok (require RT::Groups);
+
+=end testing
=cut
@@ -84,6 +88,24 @@ use RT::Users;
# {{{ sub _Init
+=begin testing
+
+# next had bugs
+# Groups->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => xx );
+my $g = RT::Group->new($RT::SystemUser);
+my ($id, $msg) = $g->CreateUserDefinedGroup(Name => 'GroupsNotEqualTest');
+ok ($id, "created group #". $g->id) or diag("error: $msg");
+
+my $groups = RT::Groups->new($RT::SystemUser);
+$groups->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $g->id );
+$groups->LimitToUserDefinedGroups();
+my $bug = grep $_->id == $g->id, @{$groups->ItemsArrayRef};
+ok (!$bug, "didn't find group");
+
+=end testing
+
+=cut
+
sub _Init {
my $self = shift;
$self->{'table'} = "Groups";
@@ -150,7 +172,7 @@ sub LimitToSystemInternalGroups {
# {{{ LimiToUserDefinedGroups
-=head2 LimitToUserDefinedGroups
+=head2 LimitToUserDefined Groups
Return only UserDefined Groups
@@ -167,7 +189,7 @@ sub LimitToUserDefinedGroups {
# }}}
-# {{{ LimiToPersonalGroupsFor
+# {{{ LimiToPersonalGroups
=head2 LimitToPersonalGroupsFor PRINCIPAL_ID
@@ -243,6 +265,27 @@ sub LimitToRolesForSystem {
Limits the set of groups returned to groups which have
Principal PRINCIPAL_ID as a member
+
+=begin testing
+
+my $u = RT::User->new($RT::SystemUser);
+$u->Create(Name => 'Membertests');
+my $g = RT::Group->new($RT::SystemUser);
+my ($id, $msg) = $g->CreateUserDefinedGroup(Name => 'Membertests');
+ok ($id, $msg);
+
+my ($aid, $amsg) =$g->AddMember($u->id);
+ok ($aid, $amsg);
+ok($g->HasMember($u->PrincipalObj),"G has member u");
+
+my $groups = RT::Groups->new($RT::SystemUser);
+$groups->LimitToUserDefinedGroups();
+$groups->WithMember(PrincipalId => $u->id);
+ok ($groups->Count == 1,"found the 1 group - " . $groups->Count);
+ok ($groups->First->Id == $g->Id, "it's the right one");
+
+=end testing
+
=cut
@@ -264,42 +307,92 @@ sub WithMember {
$self->Limit(ALIAS => $members, FIELD => 'MemberId', OPERATOR => '=', VALUE => $args{'PrincipalId'});
}
-sub WithoutMember {
- my $self = shift;
- my %args = (
- PrincipalId => undef,
- Recursively => undef,
- @_
- );
-
- my $members = $args{'Recursively'} ? 'CachedGroupMembers' : 'GroupMembers';
- my $members_alias = $self->Join(
- TYPE => 'LEFT',
- FIELD1 => 'id',
- TABLE2 => $members,
- FIELD2 => 'GroupId',
- );
- $self->Limit(
- LEFTJOIN => $members_alias,
- ALIAS => $members_alias,
- FIELD => 'MemberId',
- OPERATOR => '=',
- VALUE => $args{'PrincipalId'},
- );
- $self->Limit(
- ALIAS => $members_alias,
- FIELD => 'MemberId',
- OPERATOR => 'IS',
- VALUE => 'NULL',
- QUOTEVALUE => 0,
- );
-}
=head2 WithRight { Right => RIGHTNAME, Object => RT::Record, IncludeSystemRights => 1, IncludeSuperusers => 0, EquivObjects => [ ] }
Find all groups which have RIGHTNAME for RT::Record. Optionally include global rights and superusers. By default, include the global rights, but not the superusers.
+=begin testing
+
+my $q = RT::Queue->new($RT::SystemUser);
+my ($id, $msg) =$q->Create( Name => 'GlobalACLTest');
+ok ($id, $msg);
+
+my $testuser = RT::User->new($RT::SystemUser);
+($id,$msg) = $testuser->Create(Name => 'JustAnAdminCc');
+ok ($id,$msg);
+
+my $global_admin_cc = RT::Group->new($RT::SystemUser);
+$global_admin_cc->LoadSystemRoleGroup('AdminCc');
+ok($global_admin_cc->id, "Found the global admincc group");
+my $groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'OwnTicket', Object => $q);
+is($groups->Count, 1);
+($id, $msg) = $global_admin_cc->PrincipalObj->GrantRight(Right =>'OwnTicket', Object=> $RT::System);
+ok ($id,$msg);
+ok (!$testuser->HasRight(Object => $q, Right => 'OwnTicket') , "The test user does not have the right to own tickets in the test queue");
+($id, $msg) = $q->AddWatcher(Type => 'AdminCc', PrincipalId => $testuser->id);
+ok($id,$msg);
+ok ($testuser->HasRight(Object => $q, Right => 'OwnTicket') , "The test user does have the right to own tickets now. thank god.");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'OwnTicket', Object => $q);
+ok ($id,$msg);
+is($groups->Count, 3);
+
+my $RTxGroup = RT::Group->new($RT::SystemUser);
+($id, $msg) = $RTxGroup->CreateUserDefinedGroup( Name => 'RTxGroup', Description => "RTx extension group");
+ok ($id,$msg);
+
+my $RTxSysObj = {};
+bless $RTxSysObj, 'RTx::System';
+*RTx::System::Id = sub { 1; };
+*RTx::System::id = *RTx::System::Id;
+my $ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $RTxGroup->id, PrincipalType => 'Group', RightName => 'RTxGroupRight', ObjectType => 'RTx::System', ObjectId => 1);
+ok ($id, "ACL for RTxSysObj created");
+
+my $RTxObj = {};
+bless $RTxObj, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 4; };
+*RTx::System::Record::id = *RTx::System::Record::Id;
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxSysObj);
+is($groups->Count, 1, "RTxGroupRight found for RTxSysObj");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj);
+is($groups->Count, 0, "RTxGroupRight not found for RTxObj");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj, EquivObjects => [ $RTxSysObj ]);
+is($groups->Count, 1, "RTxGroupRight found for RTxObj using EquivObjects");
+
+$ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $RTxGroup->id, PrincipalType => 'Group', RightName => 'RTxGroupRight', ObjectType => 'RTx::System::Record', ObjectId => 5 );
+ok ($id, "ACL for RTxObj created");
+
+my $RTxObj2 = {};
+bless $RTxObj2, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 5; };
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj2);
+is($groups->Count, 1, "RTxGroupRight found for RTxObj2");
+
+$groups = RT::Groups->new($RT::SystemUser);
+$groups->WithRight(Right => 'RTxGroupRight', Object => $RTxObj2, EquivObjects => [ $RTxSysObj ]);
+is($groups->Count, 1, "RTxGroupRight found for RTxObj2");
+
+
+
+=end testing
=cut
@@ -322,6 +415,7 @@ sub WithRight {
$from_group->WithGroupRight( %args );
#XXX: DIRTY HACK
+ use DBIx::SearchBuilder 1.50; #no version on ::Union :(
use DBIx::SearchBuilder::Union;
my $union = new DBIx::SearchBuilder::Union;
$union->add($from_role);
diff --git a/rt/lib/RT/Handle.pm b/rt/lib/RT/Handle.pm
index 46070cec7..5cdb65e5b 100644
--- a/rt/lib/RT/Handle.pm
+++ b/rt/lib/RT/Handle.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,1042 +14,85 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
+# 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.
#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
=head1 NAME
-RT::Handle - RT's database handle
+ RT::Handle - RT's database handle
=head1 SYNOPSIS
- use RT;
- BEGIN { RT::LoadConfig() };
- use RT::Handle;
+ use RT::Handle;
=head1 DESCRIPTION
-C<RT::Handle> is RT specific wrapper over one of L<DBIx::SearchBuilder::Handle>
-classes. As RT works with different types of DBs we subclass repsective handler
-from L<DBIx::SerachBuilder>. Type of the DB is defined by C<DatabasseType> RT's
-config option. You B<must> load this module only when the configs have been
-loaded.
-
-=cut
+=begin testing
-package RT::Handle;
+ok(require RT::Handle);
-use strict;
-use warnings;
-use vars qw/@ISA/;
+=end testing
=head1 METHODS
-=head2 FinalizeDatabaseType
-
-Sets RT::Handle's superclass to the correct subclass of
-L<DBIx::SearchBuilder::Handle>, using the C<DatabaseType> configuration.
-
=cut
-sub FinalizeDatabaseType {
- eval "use DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType') .";
- \@ISA= qw(DBIx::SearchBuilder::Handle::". RT->Config->Get('DatabaseType') .");";
+package RT::Handle;
- if ($@) {
- die "Unable to load DBIx::SearchBuilder database handle for '". RT->Config->Get('DatabaseType') ."'.\n".
- "Perhaps you've picked an invalid database type or spelled it incorrectly.\n".
- $@;
- }
-}
+use strict;
+use vars qw/@ISA/;
+
+eval "use DBIx::SearchBuilder::Handle::$RT::DatabaseType;
+\@ISA= qw(DBIx::SearchBuilder::Handle::$RT::DatabaseType);";
+#TODO check for errors here.
=head2 Connect
-Connects to RT's database using credentials and options from the RT config.
-Takes nothing.
+Connects to RT's database handle.
+Takes nothing. Calls SUPER::Connect with the needed args
=cut
sub Connect {
- my $self = shift;
-
- my $db_type = RT->Config->Get('DatabaseType');
- if ( $db_type eq 'Oracle' ) {
- $ENV{'NLS_LANG'} = "AMERICAN_AMERICA.AL32UTF8";
- $ENV{'NLS_NCHAR'} = "AL32UTF8";
- }
+my $self=shift;
- $self->SUPER::Connect(
- User => RT->Config->Get('DatabaseUser'),
- Password => RT->Config->Get('DatabasePassword'),
- );
+# Unless the database port is a positive integer, we really don't want to pass it.
- if ( $db_type eq 'mysql' ) {
- my $version = $self->DatabaseVersion;
- ($version) = $version =~ /^(\d+\.\d+)/;
- $self->dbh->do("SET NAMES 'utf8'") if $version >= 4.1;
- }
-
- $self->dbh->{'LongReadLen'} = RT->Config->Get('MaxAttachmentSize');
+$self->SUPER::Connect(
+ User => $RT::DatabaseUser,
+ Password => $RT::DatabasePassword,
+ );
+
}
-=head2 BuildDSN
+=item BuildDSN
-Build the DSN for the RT database. Doesn't take any parameters, draws all that
-from the config.
+Build the DSN for the RT database. doesn't take any parameters, draws all that
+from the config file.
=cut
-require File::Spec;
sub BuildDSN {
my $self = shift;
- # Unless the database port is a positive integer, we really don't want to pass it.
- my $db_port = RT->Config->Get('DatabasePort');
- $db_port = undef unless (defined $db_port && $db_port =~ /^(\d+)$/);
- my $db_host = RT->Config->Get('DatabaseHost');
- $db_host = undef unless $db_host;
- my $db_name = RT->Config->Get('DatabaseName');
- my $db_type = RT->Config->Get('DatabaseType');
- $db_name = File::Spec->catfile($RT::VarPath, $db_name)
- if $db_type eq 'SQLite' && !File::Spec->file_name_is_absolute($db_name);
-
- my %args = (
- Host => $db_host,
- Database => $db_name,
- Port => $db_port,
- Driver => $db_type,
- RequireSSL => RT->Config->Get('DatabaseRequireSSL'),
- DisconnectHandleOnDestroy => 1,
- );
- if ( $db_type eq 'Oracle' && $db_host ) {
- $args{'SID'} = delete $args{'Database'};
- }
- $self->SUPER::BuildDSN( %args );
-}
-
-=head2 DSN
-
-Returns the DSN for this handle. In order to get correct value you must
-build DSN first, see L</BuildDSN>.
-
-This is method can be called as class method, in this case creates
-temporary handle object, L</BuildDSN builds DSN> and returns it.
-
-=cut
-
-sub DSN {
- my $self = shift;
- return $self->SUPER::DSN if ref $self;
-
- my $handle = $self->new;
- $handle->BuildDSN;
- return $handle->DSN;
-}
-
-=head2 SystemDSN
-
-Returns a DSN suitable for database creates and drops
-and user creates and drops.
-
-Gets RT's DSN first (see L<DSN>) and then change it according
-to requirements of a database system RT's using.
-
-=cut
-
-sub SystemDSN {
- my $self = shift;
-
- my $db_name = RT->Config->Get('DatabaseName');
- my $db_type = RT->Config->Get('DatabaseType');
-
- my $dsn = $self->DSN;
- if ( $db_type eq 'mysql' ) {
- # with mysql, you want to connect sans database to funge things
- $dsn =~ s/dbname=\Q$db_name//;
- }
- elsif ( $db_type eq 'Pg' ) {
- # with postgres, you want to connect to template1 database
- $dsn =~ s/dbname=\Q$db_name/dbname=template1/;
- }
- elsif ( $db_type eq 'Informix' ) {
- # with Informix, you want to connect sans database:
- $dsn =~ s/Informix:\Q$db_name/Informix:/;
- }
- return $dsn;
-}
-
-=head2 Database compatibility and integrity checks
-
-
-
-=cut
-
-sub CheckIntegrity {
- my $self = shift;
-
- my $dsn = $self->DSN;
- my $user = RT->Config->Get('DatabaseUser');
- my $pass = RT->Config->Get('DatabasePassword');
-
- my $dbh = DBI->connect(
- $dsn, $user, $pass,
- { RaiseError => 0, PrintError => 0 },
- );
- unless ( $dbh ) {
- return (0, 'no connection', "Failed to connect to $dsn as user '$user': ". $DBI::errstr);
- }
-
- RT::ConnectToDatabase();
- RT::InitLogging();
-
- require RT::CurrentUser;
- my $test_user = new RT::CurrentUser;
- $test_user->Load('RT_System');
- unless ( $test_user->id ) {
- return (0, 'no system user', "Couldn't find RT_System user in the DB '$dsn'");
- }
-
- $test_user = new RT::CurrentUser;
- $test_user->Load('Nobody');
- unless ( $test_user->id ) {
- return (0, 'no nobody user', "Couldn't find Nobody user in the DB '$dsn'");
- }
-
- return $dbh;
-}
-
-sub CheckCompatibility {
- my $self = shift;
- my $dbh = shift;
- my $state = shift || 'post';
-
- my $db_type = RT->Config->Get('DatabaseType');
- if ( $db_type eq "mysql" ) {
- # Check which version we're running
- my $version = ($dbh->selectrow_array("show variables like 'version'"))[1];
- return (0, "couldn't get version of the mysql server")
- unless $version;
-
- ($version) = $version =~ /^(\d+\.\d+)/;
- return (0, "RT is unsupported on MySQL versions before 4.0.x, it's $version")
- if $version < 4;
-
- # MySQL must have InnoDB support
- my $innodb = ($dbh->selectrow_array("show variables like 'have_innodb'"))[1];
- if ( lc $innodb eq "no" ) {
- return (0, "RT requires that MySQL be compiled with InnoDB table support.\n".
- "See http://dev.mysql.com/doc/mysql/en/InnoDB.html");
- } elsif ( lc $innodb eq "disabled" ) {
- return (0, "RT requires that MySQL InnoDB table support be enabled.\n".
- "Remove the 'skip-innodb' line from your my.cnf file, restart MySQL, and try again.\n");
- }
-
- if ( $state eq 'post' ) {
- my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Tickets")->[1];
- unless ( $create_table =~ /(?:ENGINE|TYPE)\s*=\s*InnoDB/i ) {
- return (0, "RT requires that all its tables be of InnoDB type. Upgrade RT tables.");
- }
- }
- if ( $version >= 4.1 && $state eq 'post' ) {
- my $create_table = $dbh->selectrow_arrayref("SHOW CREATE TABLE Attachments")->[1];
- unless ( $create_table =~ /\bContent\b[^,]*BLOB/i ) {
- return (0, "RT since version 3.8 has new schema for MySQL versions after 4.1.0\n"
- ."Follow instructions in the UPGRADING.mysql file.");
- }
- }
- }
- return (1)
-}
-
-=head2 Database maintanance
-
-=head3 CreateDatabase $DBH
-
-Creates a new database. This method can be used as class method.
-
-Takes DBI handle. Many database systems require special handle to
-allow you to create a new database, so you have to use L<SystemDSN>
-method during connection.
-
-Fetches type and name of the DB from the config.
-
-=cut
-
-sub CreateDatabase {
- my $self = shift;
- my $dbh = shift or return (0, "No DBI handle provided");
- my $db_type = RT->Config->Get('DatabaseType');
- my $db_name = RT->Config->Get('DatabaseName');
-
- my $status;
- if ( $db_type eq 'SQLite' ) {
- return (1, 'Skipped as SQLite doesn\'t need any action');
- }
- elsif ( $db_type eq 'Oracle' ) {
- my $db_user = RT->Config->Get('DatabaseUser');
- my $db_pass = RT->Config->Get('DatabasePassword');
- $status = $dbh->do(
- "CREATE USER $db_user IDENTIFIED BY $db_pass"
- ." default tablespace USERS"
- ." temporary tablespace TEMP"
- ." quota unlimited on USERS"
- );
- unless ( $status ) {
- return $status, "Couldn't create user $db_user identified by $db_pass."
- ."\nError: ". $dbh->errstr;
- }
- $status = $dbh->do( "GRANT connect, resource TO $db_user" );
- unless ( $status ) {
- return $status, "Couldn't grant connect and resource to $db_user."
- ."\nError: ". $dbh->errstr;
- }
- return (1, "Created user $db_user. All RT's objects should be in his schema.");
- }
- elsif ( $db_type eq 'Pg' ) {
- # XXX: as we get external DBH we don't know if RaiseError or PrintError
- # are enabled, so we have to setup it here and restore them back
- $status = $dbh->do("CREATE DATABASE $db_name WITH ENCODING='UNICODE' TEMPLATE template0")
- || $dbh->do("CREATE DATABASE $db_name TEMPLATE template0");
- }
- elsif ( $db_type eq 'Informix' ) {
- local $ENV{'DB_LOCALE'} = 'en_us.utf8';
- $status = $dbh->do("CREATE DATABASE $db_name WITH BUFFERED LOG");
- }
- else {
- $status = $dbh->do("CREATE DATABASE $db_name");
- }
- return ($status, $DBI::errstr);
-}
-
-=head3 DropDatabase $DBH [Force => 0]
-
-Drops RT's database. This method can be used as class method.
-
-Takes DBI handle as first argument. Many database systems require
-special handle to allow you to create a new database, so you have
-to use L<SystemDSN> method during connection.
-
-Fetches type and name of the DB from the config.
-
-=cut
-
-sub DropDatabase {
- my $self = shift;
- my $dbh = shift or return (0, "No DBI handle provided");
-
- my $db_type = RT->Config->Get('DatabaseType');
- my $db_name = RT->Config->Get('DatabaseName');
-
- if ( $db_type eq 'Oracle' || $db_type eq 'Informix' ) {
- my $db_user = RT->Config->Get('DatabaseUser');
- my $status = $dbh->do( "DROP USER $db_user CASCADE" );
- unless ( $status ) {
- return 0, "Couldn't drop user $db_user."
- ."\nError: ". $dbh->errstr;
- }
- return (1, "Successfully dropped user '$db_user' with his schema.");
- }
- elsif ( $db_type eq 'SQLite' ) {
- my $path = $db_name;
- $path = "$RT::VarPath/$path" unless substr($path, 0, 1) eq '/';
- unlink $path or return (0, "Couldn't remove '$path': $!");
- return (1);
- } else {
- $dbh->do("DROP DATABASE ". $db_name)
- or return (0, $DBI::errstr);
- }
- return (1);
-}
-
-=head2 InsertACL
-
-=cut
-
-sub InsertACL {
- my $self = shift;
- my $dbh = shift;
- my $base_path = shift || $RT::EtcPath;
-
- my $db_type = RT->Config->Get('DatabaseType');
- return (1) if $db_type eq 'SQLite';
-
- $dbh = $self->dbh if !$dbh && ref $self;
- return (0, "No DBI handle provided") unless $dbh;
-
- return (0, "'$base_path' doesn't exist") unless -e $base_path;
-
- my $path;
- if ( -d $base_path ) {
- $path = File::Spec->catfile( $base_path, "acl.$db_type");
- $path = $self->GetVersionFile($dbh, $path);
-
- $path = File::Spec->catfile( $base_path, "acl")
- unless $path && -e $path;
- return (0, "Couldn't find ACLs for $db_type")
- unless -e $path;
- } else {
- $path = $base_path;
- }
-
- local *acl;
- do $path || return (0, "Couldn't load ACLs: " . $@);
- my @acl = acl($dbh);
- foreach my $statement (@acl) {
- my $sth = $dbh->prepare($statement)
- or return (0, "Couldn't prepare SQL query:\n $statement\n\nERROR: ". $dbh->errstr);
- unless ( $sth->execute ) {
- return (0, "Couldn't run SQL query:\n $statement\n\nERROR: ". $sth->errstr);
- }
- }
- return (1);
-}
-
-=head2 InsertSchema
-
-=cut
-
-sub InsertSchema {
- my $self = shift;
- my $dbh = shift;
- my $base_path = (shift || $RT::EtcPath);
-
- $dbh = $self->dbh if !$dbh && ref $self;
- return (0, "No DBI handle provided") unless $dbh;
-
- my $db_type = RT->Config->Get('DatabaseType');
-
- my $file;
- if ( -d $base_path ) {
- $file = $base_path . "/schema." . $db_type;
- } else {
- $file = $base_path;
- }
-
- $file = $self->GetVersionFile( $dbh, $file );
- unless ( $file ) {
- return (0, "Couldn't find schema file(s) '$file*'");
- }
- unless ( -f $file && -r $file ) {
- return (0, "File '$file' doesn't exist or couldn't be read");
- }
-
- my (@schema);
-
- open my $fh_schema, "<$file";
-
- my $has_local = 0;
- open my $fh_schema_local, "<" . $self->GetVersionFile( $dbh, $RT::LocalEtcPath . "/schema." . $db_type )
- and $has_local = 1;
-
- my $statement = "";
- foreach my $line ( <$fh_schema>, ($_ = ';;'), $has_local? <$fh_schema_local>: () ) {
- $line =~ s/\#.*//g;
- $line =~ s/--.*//g;
- $statement .= $line;
- if ( $line =~ /;(\s*)$/ ) {
- $statement =~ s/;(\s*)$//g;
- push @schema, $statement;
- $statement = "";
- }
- }
- close $fh_schema; close $fh_schema_local;
-
- if ( $db_type eq 'Oracle' ) {
- my $db_user = RT->Config->Get('DatabaseUser');
- my $status = $dbh->do( "ALTER SESSION SET CURRENT_SCHEMA=$db_user" );
- unless ( $status ) {
- return $status, "Couldn't set current schema to $db_user."
- ."\nError: ". $dbh->errstr;
- }
- }
-
- local $SIG{__WARN__} = sub {};
- my $is_local = 0;
- $dbh->begin_work or return (0, "Couldn't begin transaction: ". $dbh->errstr);
- foreach my $statement (@schema) {
- if ( $statement =~ /^\s*;$/ ) {
- $is_local = 1; next;
- }
-
- my $sth = $dbh->prepare($statement)
- or return (0, "Couldn't prepare SQL query:\n$statement\n\nERROR: ". $dbh->errstr);
- unless ( $sth->execute or $is_local ) {
- return (0, "Couldn't run SQL query:\n$statement\n\nERROR: ". $sth->errstr);
- }
- }
- $dbh->commit or return (0, "Couldn't commit transaction: ". $dbh->errstr);
- return (1);
-}
-
-=head1 GetVersionFile
-
-Takes base name of the file as argument, scans for <base name>-<version> named
-files and returns file name with closest version to the version of the RT DB.
-
-=cut
-
-sub GetVersionFile {
- my $self = shift;
- my $dbh = shift;
- my $base_name = shift;
+$RT::DatabasePort = undef unless (defined $RT::DatabasePort && $RT::DatabasePort =~ /^(\d+)$/);
+$RT::DatabaseHost = undef unless (defined $RT::DatabaseHost && $RT::DatabaseHost ne '');
- my $db_version = ref $self
- ? $self->DatabaseVersion
- : do {
- my $tmp = RT::Handle->new;
- $tmp->dbh($dbh);
- $tmp->DatabaseVersion;
- };
+ $self->SUPER::BuildDSN(Host => $RT::DatabaseHost,
+ Database => $RT::DatabaseName,
+ Port => $RT::DatabasePort,
+ Driver => $RT::DatabaseType,
+ RequireSSL => $RT::DatabaseRequireSSL,
+ DisconnectHandleOnDestroy => 1
+ );
+
- require File::Glob;
- my @files = File::Glob::bsd_glob("$base_name*");
- return '' unless @files;
-
- my %version = map { $_ =~ /\.\w+-([-\w\.]+)$/; ($1||0) => $_ } @files;
- my $version;
- foreach ( reverse sort cmp_version keys %version ) {
- if ( cmp_version( $db_version, $_ ) >= 0 ) {
- $version = $_;
- last;
- }
- }
-
- return defined $version? $version{ $version } : undef;
-}
-
-sub cmp_version($$) {
- my ($a, $b) = (@_);
- $b =~ s/HEAD$/9999/;
- my @a = split /[^0-9]+/, $a;
- my @b = split /[^0-9]+/, $b;
- for ( my $i = 0; $i < @a; $i++ ) {
- return 1 unless defined $b[$i];
- return $a[$i] <=> $b[$i] if $a[$i] <=> $b[$i];
- }
- return 0 if @a == @b;
- return -1;
}
-
-=head2 InsertInitialData
-
-Inserts system objects into RT's DB, like system user or 'nobody',
-internal groups and other records required. However, this method
-doesn't insert any real users like 'root' and you have to use
-InsertData or another way to do that.
-
-Takes no arguments. Returns status and message tuple.
-
-It's safe to call this method even if those objects already exist.
-
-=cut
-
-sub InsertInitialData {
- my $self = shift;
-
- my @warns;
-
- # create RT_System user and grant him rights
- {
- require RT::CurrentUser;
-
- my $test_user = RT::User->new( new RT::CurrentUser );
- $test_user->Load('RT_System');
- if ( $test_user->id ) {
- push @warns, "Found system user in the DB.";
- }
- else {
- my $user = RT::User->new( new RT::CurrentUser );
- my ( $val, $msg ) = $user->_BootstrapCreate(
- Name => 'RT_System',
- RealName => 'The RT System itself',
- Comments => 'Do not delete or modify this user. '
- . 'It is integral to RT\'s internal database structures',
- Creator => '1',
- LastUpdatedBy => '1',
- );
- return ($val, $msg) unless $val;
- }
- DBIx::SearchBuilder::Record::Cachable->FlushCache;
- }
-
- # init RT::SystemUser and RT::System objects
- RT::InitSystemObjects();
- unless ( $RT::SystemUser->id ) {
- return (0, "Couldn't load system user");
- }
-
- # grant SuperUser right to system user
- {
- my $test_ace = RT::ACE->new( $RT::SystemUser );
- $test_ace->LoadByCols(
- PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ),
- PrincipalType => 'Group',
- RightName => 'SuperUser',
- ObjectType => 'RT::System',
- ObjectId => 1,
- );
- if ( $test_ace->id ) {
- push @warns, "System user has global SuperUser right.";
- } else {
- my $ace = RT::ACE->new( $RT::SystemUser );
- my ( $val, $msg ) = $ace->_BootstrapCreate(
- PrincipalId => ACLEquivGroupId( $RT::SystemUser->Id ),
- PrincipalType => 'Group',
- RightName => 'SuperUser',
- ObjectType => 'RT::System',
- ObjectId => 1,
- );
- return ($val, $msg) unless $val;
- }
- DBIx::SearchBuilder::Record::Cachable->FlushCache;
- }
-
- # system groups
- # $self->loc('Everyone'); # For the string extractor to get a string to localize
- # $self->loc('Privileged'); # For the string extractor to get a string to localize
- # $self->loc('Unprivileged'); # For the string extractor to get a string to localize
- foreach my $name (qw(Everyone Privileged Unprivileged)) {
- my $group = RT::Group->new( $RT::SystemUser );
- $group->LoadSystemInternalGroup( $name );
- if ( $group->id ) {
- push @warns, "System group '$name' already exists.";
- next;
- }
-
- $group = RT::Group->new( $RT::SystemUser );
- my ( $val, $msg ) = $group->_Create(
- Type => $name,
- Domain => 'SystemInternal',
- Description => 'Pseudogroup for internal use', # loc
- Name => '',
- Instance => '',
- );
- return ($val, $msg) unless $val;
- }
-
- # nobody
- {
- my $user = RT::User->new( $RT::SystemUser );
- $user->Load('Nobody');
- if ( $user->id ) {
- push @warns, "Found 'Nobody' user in the DB.";
- }
- else {
- my ( $val, $msg ) = $user->Create(
- Name => 'Nobody',
- RealName => 'Nobody in particular',
- Comments => 'Do not delete or modify this user. It is integral '
- .'to RT\'s internal data structures',
- Privileged => 0,
- );
- return ($val, $msg) unless $val;
- }
-
- if ( $user->HasRight( Right => 'OwnTicket', Object => $RT::System ) ) {
- push @warns, "User 'Nobody' has global OwnTicket right.";
- } else {
- my ( $val, $msg ) = $user->PrincipalObj->GrantRight(
- Right => 'OwnTicket',
- Object => $RT::System,
- );
- return ($val, $msg) unless $val;
- }
- }
-
- # rerun to get init Nobody as well
- RT::InitSystemObjects();
-
- # system role groups
- foreach my $name (qw(Owner Requestor Cc AdminCc)) {
- my $group = RT::Group->new( $RT::SystemUser );
- $group->LoadSystemRoleGroup( $name );
- if ( $group->id ) {
- push @warns, "System role '$name' already exists.";
- next;
- }
-
- $group = RT::Group->new( $RT::SystemUser );
- my ( $val, $msg ) = $group->_Create(
- Type => $name,
- Domain => 'RT::System-Role',
- Description => 'SystemRolegroup for internal use', # loc
- Name => '',
- Instance => '',
- );
- return ($val, $msg) unless $val;
- }
-
- push @warns, "You appear to have a functional RT database."
- if @warns;
-
- return (1, join "\n", @warns);
-}
-
-=head2 InsertData
-
-Load some sort of data into the database, takes path to a file.
-
-=cut
-
-sub InsertData {
- my $self = shift;
- my $datafile = shift;
-
- # Slurp in stuff to insert from the datafile. Possible things to go in here:-
- our (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
- @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
- local (@Groups, @Users, @ACL, @Queues, @ScripActions, @ScripConditions,
- @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
-
- local $@;
- $RT::Logger->debug("Going to load '$datafile' data file");
- eval { require $datafile }
- or return (0, "Couldn't load data from '$datafile' for import:\n\nERROR:". $@);
-
- if ( @Initial ) {
- $RT::Logger->debug("Running initial actions...");
- foreach ( @Initial ) {
- local $@;
- eval { $_->(); 1 } or return (0, "One of initial functions failed: $@");
- }
- $RT::Logger->debug("Done.");
- }
- if ( @Groups ) {
- $RT::Logger->debug("Creating groups...");
- foreach my $item (@Groups) {
- my $new_entry = RT::Group->new( $RT::SystemUser );
- my $member_of = delete $item->{'MemberOf'};
- my ( $return, $msg ) = $new_entry->_Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- next;
- } else {
- $RT::Logger->debug($return .".");
- }
- if ( $member_of ) {
- $member_of = [ $member_of ] unless ref $member_of eq 'ARRAY';
- foreach( @$member_of ) {
- my $parent = RT::Group->new($RT::SystemUser);
- if ( ref $_ eq 'HASH' ) {
- $parent->LoadByCols( %$_ );
- }
- elsif ( !ref $_ ) {
- $parent->LoadUserDefinedGroup( $_ );
- }
- else {
- $RT::Logger->error(
- "(Error: wrong format of MemberOf field."
- ." Should be name of user defined group or"
- ." hash reference with 'column => value' pairs."
- ." Use array reference to add to multiple groups)"
- );
- next;
- }
- unless ( $parent->Id ) {
- $RT::Logger->error("(Error: couldn't load group to add member)");
- next;
- }
- my ( $return, $msg ) = $parent->AddMember( $new_entry->Id );
- unless ( $return ) {
- $RT::Logger->error( $msg );
- } else {
- $RT::Logger->debug( $return ."." );
- }
- }
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @Users ) {
- $RT::Logger->debug("Creating users...");
- foreach my $item (@Users) {
- my $new_entry = new RT::User( $RT::SystemUser );
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- } else {
- $RT::Logger->debug( $return ."." );
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @Queues ) {
- $RT::Logger->debug("Creating queues...");
- for my $item (@Queues) {
- my $new_entry = new RT::Queue($RT::SystemUser);
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- } else {
- $RT::Logger->debug( $return ."." );
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @CustomFields ) {
- $RT::Logger->debug("Creating custom fields...");
- for my $item ( @CustomFields ) {
- my $new_entry = new RT::CustomField( $RT::SystemUser );
- my $values = delete $item->{'Values'};
-
- my @queues;
- # if ref then it's list of queues, so we do things ourself
- if ( exists $item->{'Queue'} && ref $item->{'Queue'} ) {
- $item->{'LookupType'} = 'RT::Queue-RT::Ticket';
- @queues = @{ delete $item->{'Queue'} };
- }
-
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless( $return ) {
- $RT::Logger->error( $msg );
- next;
- }
-
- foreach my $value ( @{$values} ) {
- my ( $return, $msg ) = $new_entry->AddValue(%$value);
- $RT::Logger->error( $msg ) unless $return;
- }
-
- # apply by default
- if ( !@queues && !exists $item->{'Queue'} && $item->{LookupType} ) {
- my $ocf = RT::ObjectCustomField->new($RT::SystemUser);
- $ocf->Create( CustomField => $new_entry->Id );
- }
-
- for my $q (@queues) {
- my $q_obj = RT::Queue->new($RT::SystemUser);
- $q_obj->Load($q);
- unless ( $q_obj->Id ) {
- $RT::Logger->error("Could not find queue ". $q );
- next;
- }
- my $OCF = RT::ObjectCustomField->new($RT::SystemUser);
- ( $return, $msg ) = $OCF->Create(
- CustomField => $new_entry->Id,
- ObjectId => $q_obj->Id,
- );
- $RT::Logger->error( $msg ) unless $return and $OCF->Id;
- }
- }
-
- $RT::Logger->debug("done.");
- }
- if ( @ACL ) {
- $RT::Logger->debug("Creating ACL...");
- for my $item (@ACL) {
-
- my ($princ, $object);
-
- # Global rights or Queue rights?
- if ( $item->{'CF'} ) {
- $object = RT::CustomField->new( $RT::SystemUser );
- my @columns = ( Name => $item->{'CF'} );
- push @columns, Queue => $item->{'Queue'} if $item->{'Queue'} and not ref $item->{'Queue'};
- $object->LoadByName( @columns );
- } elsif ( $item->{'Queue'} ) {
- $object = RT::Queue->new($RT::SystemUser);
- $object->Load( $item->{'Queue'} );
- } else {
- $object = $RT::System;
- }
-
- $RT::Logger->error("Couldn't load object") and next unless $object and $object->Id;
-
- # Group rights or user rights?
- if ( $item->{'GroupDomain'} ) {
- $princ = RT::Group->new($RT::SystemUser);
- if ( $item->{'GroupDomain'} eq 'UserDefined' ) {
- $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
- } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) {
- $princ->LoadSystemInternalGroup( $item->{'GroupType'} );
- } elsif ( $item->{'GroupDomain'} eq 'RT::System-Role' ) {
- $princ->LoadSystemRoleGroup( $item->{'GroupType'} );
- } elsif ( $item->{'GroupDomain'} eq 'RT::Queue-Role' &&
- $item->{'Queue'} )
- {
- $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'},
- Queue => $object->id);
- } else {
- $princ->Load( $item->{'GroupId'} );
- }
- } else {
- $princ = RT::User->new($RT::SystemUser);
- $princ->Load( $item->{'UserId'} );
- }
-
- # Grant it
- my ( $return, $msg ) = $princ->PrincipalObj->GrantRight(
- Right => $item->{'Right'},
- Object => $object
- );
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
- $RT::Logger->debug("done.");
- }
-
- if ( @ScripActions ) {
- $RT::Logger->debug("Creating ScripActions...");
-
- for my $item (@ScripActions) {
- my $new_entry = RT::ScripAction->new($RT::SystemUser);
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
-
- $RT::Logger->debug("done.");
- }
-
- if ( @ScripConditions ) {
- $RT::Logger->debug("Creating ScripConditions...");
-
- for my $item (@ScripConditions) {
- my $new_entry = RT::ScripCondition->new($RT::SystemUser);
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
-
- $RT::Logger->debug("done.");
- }
-
- if ( @Templates ) {
- $RT::Logger->debug("Creating templates...");
-
- for my $item (@Templates) {
- my $new_entry = new RT::Template($RT::SystemUser);
- my ( $return, $msg ) = $new_entry->Create(%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @Scrips ) {
- $RT::Logger->debug("Creating scrips...");
-
- for my $item (@Scrips) {
- my $new_entry = new RT::Scrip($RT::SystemUser);
-
- my @queues = ref $item->{'Queue'} eq 'ARRAY'? @{ $item->{'Queue'} }: $item->{'Queue'} || 0;
- push @queues, 0 unless @queues; # add global queue at least
-
- foreach my $q ( @queues ) {
- my ( $return, $msg ) = $new_entry->Create( %$item, Queue => $q );
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @Attributes ) {
- $RT::Logger->debug("Creating predefined searches...");
- my $sys = RT::System->new($RT::SystemUser);
-
- for my $item (@Attributes) {
- my $obj = delete $item->{Object}; # XXX: make this something loadable
- $obj ||= $sys;
- my ( $return, $msg ) = $obj->AddAttribute (%$item);
- unless ( $return ) {
- $RT::Logger->error( $msg );
- }
- else {
- $RT::Logger->debug( $return ."." );
- }
- }
- $RT::Logger->debug("done.");
- }
- if ( @Final ) {
- $RT::Logger->debug("Running final actions...");
- for ( @Final ) {
- local $@;
- eval { $_->(); };
- $RT::Logger->error( "Failed to run one of final actions: $@" )
- if $@;
- }
- $RT::Logger->debug("done.");
- }
-
- my $db_type = RT->Config->Get('DatabaseType');
- $RT::Handle->Disconnect() unless $db_type eq 'SQLite';
-
- $RT::Logger->debug("Done setting up database content.");
-
-# TODO is it ok to return 1 here? If so, the previous codes in this sub
-# should return (0, $msg) if error happens instead of just warning.
-# anyway, we need to return something here to tell if everything is ok
- return( 1, 'Done inserting data' );
-}
-
-=head2 ACLEquivGroupId
-
-Given a userid, return that user's acl equivalence group
-
-=cut
-
-sub ACLEquivGroupId {
- my $id = shift;
-
- my $cu = $RT::SystemUser;
- unless ( $cu ) {
- require RT::CurrentUser;
- $cu = new RT::CurrentUser;
- $cu->LoadByName('RT_System');
- warn "Couldn't load RT_System user" unless $cu->id;
- }
-
- my $equiv_group = RT::Group->new( $cu );
- $equiv_group->LoadACLEquivalenceGroup( $id );
- return $equiv_group->Id;
-}
-
-__PACKAGE__->FinalizeDatabaseType;
-
eval "require RT::Handle_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Handle_Vendor.pm});
eval "require RT::Handle_Local";
diff --git a/rt/lib/RT/I18N/en_malkovich.po b/rt/lib/RT/I18N/en_malkovich.po
deleted file mode 100644
index 74769f1a3..000000000
--- a/rt/lib/RT/I18N/en_malkovich.po
+++ /dev/null
@@ -1,3973 +0,0 @@
-msgid ""
-msgstr ""
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: html/Approvals/Elements/Approve:26 html/Approvals/Elements/ShowDependency:49 html/SelfService/Display.html:24 html/Ticket/Display.html:25 html/Ticket/Display.html:29
-#. ($TicketObj->Id, $TicketObj->Subject)
-#. ($Ticket->id, $Ticket->Subject)
-#. ($ticket->Id, $ticket->Subject)
-#. ($link->BaseObj->Id, $link->BaseObj->Subject)
-msgid "#%1: %2"
-msgstr "#%1: %2"
-
-#: html/Search/Elements/SelectPersonType:30 lib/RT/Date.pm:337
-#. ($s, $time_unit)
-#. ($option, $subtype)
-msgid "%1 %2"
-msgstr "%1 %2"
-
-#: lib/RT/Tickets_Overlay.pm:828
-#. ($args{'FIELD'}, $args{'OPERATOR'}, $args{'VALUE'})
-msgid "%1 %2 %3"
-msgstr "%1 %2 %3"
-
-#: lib/RT/Date.pm:373
-#. ($self->GetWeekday($wday), $self->GetMonth($mon), map {sprintf "%02d", $_} ($mday, $hour, $min, $sec), ($year+1900))
-msgid "%1 %2 %3 %4:%5:%6 %7"
-msgstr "%1 %2 %3 %4:%5:%6 %7"
-
-#: lib/RT/Ticket_Overlay.pm:3451 lib/RT/Transaction_Overlay.pm:550 lib/RT/Transaction_Overlay.pm:593
-#. ($cf->Name, $new_value->Content)
-#. ($field, $self->NewValue)
-#. ($self->Field, $principal->Object->Name)
-msgid "%1 %2 added"
-msgstr "%1 %2 Malkovich"
-
-#: lib/RT/Date.pm:334
-#. ($s, $time_unit)
-msgid "%1 %2 ago"
-msgstr "%1 %2 ago"
-
-#: lib/RT/Ticket_Overlay.pm:3457 lib/RT/Transaction_Overlay.pm:557
-#. ($cf->Name, $old_value, $new_value->Content)
-#. ($field, $self->OldValue, $self->NewValue)
-msgid "%1 %2 changed to %3"
-msgstr "%1 %2 Malkovich to %3"
-
-#: lib/RT/Ticket_Overlay.pm:3454 lib/RT/Transaction_Overlay.pm:553 lib/RT/Transaction_Overlay.pm:599
-#. ($cf->Name, $old_value)
-#. ($field, $self->OldValue)
-#. ($self->Field, $principal->Object->Name)
-msgid "%1 %2 deleted"
-msgstr "%1 %2 Malkovich"
-
-#: html/Admin/Elements/EditScrips:43 html/Admin/Elements/ListGlobalScrips:27 html/Ticket/Elements/PreviewScrips:53
-#. ($scrip->ConditionObj->Name, $scrip->ActionObj->Name, $scrip->TemplateObj->Name)
-#. (loc($scrip->ConditionObj->Name), loc($scrip->ActionObj->Name), loc($scrip->TemplateObj->Name))
-msgid "%1 %2 with template %3"
-msgstr "%1 %2 Malkovich %3"
-
-#: bin/rt-crontool:165 bin/rt-crontool:172 bin/rt-crontool:178
-#. ("--search-argument", "--search")
-#. ("--condition-argument", "--condition")
-#. ("--action-argument", "--action")
-msgid "%1 - An argument to pass to %2"
-msgstr "%1 - A Malkovich to pass to %2"
-
-#: bin/rt-crontool:181
-#. ("--verbose")
-msgid "%1 - Output status updates to STDOUT"
-msgstr "%1 - Malkovich Malkovich to MALKOVICH"
-
-#: bin/rt-crontool:175
-#. ("--action")
-msgid "%1 - Specify the action module you want to use"
-msgstr "%1 - Malkovich the Malkovich Malkovich to use"
-
-#: bin/rt-crontool:169
-#. ("--condition")
-msgid "%1 - Specify the condition module you want to use"
-msgstr "%1 - Malkovich the Malkovich Malkovich to use"
-
-#: bin/rt-crontool:162
-#. ("--search")
-msgid "%1 - Specify the search module you want to use"
-msgstr "%1 - Malkovich the Malkovich Malkovich to use"
-
-#: lib/RT/ScripAction_Overlay.pm:114
-#. ($self->Id)
-msgid "%1 ScripAction loaded"
-msgstr "%1 Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3484
-#. ($args{'Value'}, $cf->Name)
-msgid "%1 added as a value for %2"
-msgstr "%1 Malkovich as a Malkovich %2"
-
-#: lib/RT/Link_Overlay.pm:111 lib/RT/Link_Overlay.pm:118
-#. ($args{'Base'})
-#. ($args{'Target'})
-msgid "%1 appears to be a local object, but can't be found in the database"
-msgstr "%1 Malkovich to be a Malkovich, but can't be Malkovich in the Malkovich"
-
-#: html/Ticket/Elements/ShowDates:52 lib/RT/Transaction_Overlay.pm:458
-#. ($self->BriefDescription , $self->CreatorObj->Name)
-#. ($Ticket->LastUpdatedAsString, $Ticket->LastUpdatedByObj->Name)
-msgid "%1 by %2"
-msgstr "%1 by %2"
-
-#: lib/RT/Transaction_Overlay.pm:512 lib/RT/Transaction_Overlay.pm:688 lib/RT/Transaction_Overlay.pm:697 lib/RT/Transaction_Overlay.pm:700
-#. ($self->Field , ( $self->OldValue || $no_value ) , $self->NewValue)
-#. ($self->Field , $q1->Name , $q2->Name)
-#. ($self->Field, $t2->AsString, $t1->AsString)
-#. ($self->Field, $self->OldValue, $self->NewValue)
-msgid "%1 changed from %2 to %3"
-msgstr "%1 Malkovich %2 to %3"
-
-#: lib/RT/Record.pm:739
-msgid "%1 could not be set to %2."
-msgstr "%1 Malkovich be set to %2."
-
-#: lib/RT/Ticket_Overlay.pm:2739
-#. ($self)
-msgid "%1 couldn't set status to resolved. RT's Database may be inconsistent."
-msgstr "%1 couldn't Malkovich to Malkovich. RT's Malkovich be Malkovich."
-
-#: NOT FOUND IN SOURCE
-msgid "%1 highest priority tickets I own..."
-msgstr "%1 Malkovich Malkovich I Malkovich..."
-
-#: html/Elements/MyTickets:26
-#. ($rows)
-msgid "%1 highest priority tickets I requested..."
-msgstr "%1 Malkovich Malkovich I Malkovich..."
-
-#: bin/rt-crontool:157
-#. ($0)
-msgid "%1 is a tool to act on tickets from an external scheduling tool, such as cron."
-msgstr "%1 is a tool to act on Malkovich a Malkovich Malkovich, such as cron."
-
-#: lib/RT/Queue_Overlay.pm:784
-#. ($principal->Object->Name, $args{'Type'})
-msgid "%1 is no longer a %2 for this queue."
-msgstr "%1 is no Malkovich a %2 Malkovich."
-
-#: lib/RT/Ticket_Overlay.pm:3540
-#. ($args{'Value'}, $cf->Name)
-msgid "%1 is no longer a value for custom field %2"
-msgstr "%1 is no Malkovich a Malkovich Malkovich %2"
-
-#: html/Ticket/Create.html:155 html/Ticket/Create.html:156 html/Ticket/Elements/ShowBasics:36 html/Ticket/Elements/ShowBasics:42 html/Ticket/Elements/ShowBasics:47
-#. ('<input size=3 name="TimeWorked" value="'.$ARGS{TimeWorked}.'">')
-#. ('<input size=3 name="TimeLeft" value="'.$ARGS{TimeLeft}.'">')
-#. ($Ticket->TimeEstimated)
-#. ($Ticket->TimeWorked)
-#. ($Ticket->TimeLeft)
-msgid "%1 min"
-msgstr "%1 min"
-
-#: html/User/Elements/DelegateRights:75
-#. (loc($ObjectType =~ /^RT::(.*)$/))
-msgid "%1 rights"
-msgstr "%1 Malkovich"
-
-#: lib/RT/Action/ResolveMembers.pm:41
-#. (ref $self)
-msgid "%1 will resolve all members of a resolved group ticket."
-msgstr "%1 Malkovich Malkovich of a Malkovich Malkovich."
-
-#: lib/RT/Transaction_Overlay.pm:408
-#. ($self)
-msgid "%1: no attachment specified"
-msgstr "%1: no Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowTransactionAttachments:56
-#. ($size)
-msgid "%1b"
-msgstr "%1b"
-
-#: html/Ticket/Elements/ShowTransactionAttachments:53
-#. (int( $size / 102.4 ) / 10)
-msgid "%1k"
-msgstr "%1k"
-
-#: lib/RT/Ticket_Overlay.pm:1252
-#. ($args{'Status'})
-msgid "'%1' is an invalid value for status"
-msgstr "'%1' is a Malkovich Malkovich"
-
-#: html/Admin/Elements/EditCustomFieldValues:24 html/Admin/Elements/EditQueueWatchers:28 html/Admin/Elements/EditScrips:34 html/Admin/Elements/EditTemplates:35 html/Admin/Groups/Members.html:51 html/Elements/EditLinks:32 html/Ticket/Elements/EditPeople:45 html/User/Groups/Members.html:54
-msgid "(Check box to delete)"
-msgstr "(Malkovich to Malkovich)"
-
-#: html/Ticket/Elements/PreviewScrips:49
-msgid "(Check boxes to disable notifications to the listed recipients)"
-msgstr "(Malkovich to Malkovich Malkovich to the Malkovich Malkovich)"
-
-#: html/Ticket/Elements/PreviewScrips:71
-msgid "(Check boxes to enable notifications to the listed recipients)"
-msgstr "(Malkovich to Malkovich Malkovich to the Malkovich Malkovich)"
-
-#: NOT FOUND IN SOURCE
-msgid "(Enter ticket ids or URLs, seperated with spaces)"
-msgstr "(Malkovich Malkovich or URLs, Malkovich Malkovich)"
-
-#: html/Admin/Queues/Modify.html:53 html/Admin/Queues/Modify.html:59
-#. ($RT::CorrespondAddress)
-#. ($RT::CommentAddress)
-msgid "(If left blank, will default to %1"
-msgstr "(If Malkovich, Malkovich to %1"
-
-#: html/Admin/Elements/EditCustomFields:32 html/Admin/Elements/ListGlobalCustomFields:31
-msgid "(No custom fields)"
-msgstr "(No Malkovich)"
-
-#: html/Admin/Groups/Members.html:49 html/User/Groups/Members.html:52
-msgid "(No members)"
-msgstr "(No Malkovich)"
-
-#: html/Admin/Elements/EditScrips:31 html/Admin/Elements/ListGlobalScrips:31
-msgid "(No scrips)"
-msgstr "(No Malkovich)"
-
-#: html/Admin/Elements/EditTemplates:30
-msgid "(No templates)"
-msgstr "(No Malkovich)"
-
-#: html/Ticket/Update.html:66
-msgid "(Sends a blind carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
-msgstr "(Malkovich a Malkovich-copy of Malkovich to a Malkovich-Malkovich of Malkovich. Does <b>not</b> Malkovich Malkovich Malkovich Malkovich.)"
-
-#: html/Ticket/Create.html:78
-msgid "(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <b>will</b> receive future updates.)"
-msgstr "(Malkovich a Malkovich-copy of Malkovich to a Malkovich-Malkovich of Malkovich Malkovich Malkovich. Malkovich <b>will</b> Malkovich Malkovich.)"
-
-#: html/Ticket/Update.html:62
-msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. Does <b>not</b> change who will receive future updates.)"
-msgstr "(Malkovich a Malkovich-copy of Malkovich to a Malkovich-Malkovich of Malkovich. Does <b>not</b> Malkovich Malkovich Malkovich Malkovich.)"
-
-#: html/Ticket/Create.html:68
-msgid "(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <b>will</b> receive future updates.)"
-msgstr "(Malkovich a Malkovich-copy of Malkovich to a Malkovich-Malkovich of Malkovich. Malkovich <b>will</b> Malkovich Malkovich.)"
-
-#: html/Admin/Groups/index.html:32 html/User/Groups/index.html:32
-msgid "(empty)"
-msgstr "(Malkovich)"
-
-#: html/Admin/Users/index.html:38
-msgid "(no name listed)"
-msgstr "(no Malkovich)"
-
-#: html/Admin/Elements/SelectRights:47 html/Elements/SelectCustomFieldValue:29 html/Ticket/Elements/EditCustomField:64 html/Ticket/Elements/ShowCustomFields:35 lib/RT/Transaction_Overlay.pm:511
-msgid "(no value)"
-msgstr "(no Malkovich)"
-
-#: html/Elements/EditLinks:105 html/Ticket/Elements/BulkLinks:27
-msgid "(only one ticket)"
-msgstr "(Malkovich)"
-
-#: html/Elements/TicketList:167
-msgid "(pending approval)"
-msgstr "(Malkovich Malkovich)"
-
-#: html/Elements/TicketList:170
-msgid "(pending other Collection)"
-msgstr "(Malkovich Malkovich)"
-
-#: NOT FOUND IN SOURCE
-msgid "(pending other tickets)"
-msgstr "(Malkovich Malkovich)"
-
-#: html/Admin/Users/Modify.html:49
-msgid "(required)"
-msgstr "(Malkovich)"
-
-#: html/Ticket/Elements/ShowTransactionAttachments:60
-msgid "(untitled)"
-msgstr "(Malkovich)"
-
-#: NOT FOUND IN SOURCE
-msgid "..."
-msgstr "..."
-
-#: html/Ticket/Elements/ShowBasics:31
-msgid "<% $Ticket->Status%>"
-msgstr "<% $Ticket->Status %>"
-
-#: html/Elements/SelectTicketTypes:26
-msgid "<% $_ %>"
-msgstr "<% $_ %>"
-
-#: docs/design_docs/string-extraction-guide.txt:54 html/Elements/CreateTicket:25 lib/RT/StyleGuide.pod:767
-#. ($m->scomp('/Elements/SelectNewTicketQueue'))
-msgid "<input type=\"submit\" value=\"New ticket in\">&nbsp;%1"
-msgstr "<input type=\"submit\" value=\"Malkovich in\">&nbsp;%1"
-
-#: etc/initialdata:218
-msgid "A blank template"
-msgstr "A Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:156 lib/RT/Principal_Overlay.pm:180
-msgid "ACE not found"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:830
-msgid "ACEs can only be created and deleted."
-msgstr "Malkovich be Malkovich and Malkovich."
-
-#: NOT FOUND IN SOURCE
-msgid "Aborting to avoid unintended ticket modifications.\\n"
-msgstr "Malkovich to Malkovich Malkovich Malkovich Malkovich.\\n"
-
-#: html/User/Elements/Tabs:31
-msgid "About me"
-msgstr "Malkovich me"
-
-#: html/Admin/Users/Modify.html:79
-msgid "Access control"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrip:49
-msgid "Action"
-msgstr "Malkovich"
-
-#: lib/RT/Scrip_Overlay.pm:148
-#. ($args{'ScripAction'})
-msgid "Action %1 not found"
-msgstr "Malkovich %1 Malkovich"
-
-#: bin/rt-crontool:119
-msgid "Action committed."
-msgstr "Malkovich Malkovich."
-
-#: bin/rt-crontool:115
-msgid "Action prepared..."
-msgstr "Malkovich..."
-
-#: html/Search/Bulk.html:93
-msgid "Add AdminCc"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:89
-msgid "Add Cc"
-msgstr "Add Cc"
-
-#: html/Ticket/Create.html:113 html/Ticket/Update.html:81
-msgid "Add More Files"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:85
-msgid "Add Requestor"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/AddCustomFieldValue:24
-msgid "Add Value"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Scrip.html:54
-msgid "Add a scrip which will apply to all queues"
-msgstr "Add a Malkovich Malkovich to Malkovich"
-
-#: html/Search/Bulk.html:125
-msgid "Add comments or replies to selected tickets"
-msgstr "Malkovich or Malkovich to Malkovich Malkovich"
-
-#: html/Admin/Groups/Members.html:41 html/User/Groups/Members.html:38
-msgid "Add members"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/People.html:65 html/Ticket/Elements/AddWatchers:27
-msgid "Add new watchers"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:684
-#. ($args{'Type'})
-msgid "Added principal as a %1 for this queue"
-msgstr "Malkovich as a %1 Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1547
-#. ($self->loc($args{'Type'}))
-msgid "Added principal as a %1 for this ticket"
-msgstr "Malkovich as a %1 Malkovich"
-
-#: html/Admin/Users/Modify.html:119 html/User/Prefs.html:111
-msgid "Address1"
-msgstr "Malkovich1"
-
-#: html/Admin/Users/Modify.html:124 html/User/Prefs.html:115
-msgid "Address2"
-msgstr "Malkovich2"
-
-#: html/Ticket/Create.html:73
-msgid "Admin Cc"
-msgstr "Malkovich Cc"
-
-#: etc/initialdata:295
-msgid "Admin Comment"
-msgstr "Malkovich"
-
-#: etc/initialdata:274
-msgid "Admin Correspondence"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/index.html:24 html/Admin/Queues/index.html:27
-msgid "Admin queues"
-msgstr "Malkovich"
-
-#: html/Admin/Global/index.html:25 html/Admin/Global/index.html:27
-msgid "Admin/Global configuration"
-msgstr "Malkovich/Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Admin/Queue/Basics"
-msgstr "Malkovich/Malkovich/Malkovich"
-
-#: etc/initialdata:56 html/Ticket/Elements/ShowPeople:38 lib/RT/ACE_Overlay.pm:88
-msgid "AdminCc"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:73
-msgid "AdminCustomFields"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Group_Overlay.pm:146
-msgid "AdminGroup"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:148
-msgid "AdminGroupMembership"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/System.pm:58
-msgid "AdminOwnPersonalGroups"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:69
-msgid "AdminQueue"
-msgstr "Malkovich"
-
-#: lib/RT/System.pm:59
-msgid "AdminUsers"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/People.html:47 html/Ticket/Elements/EditPeople:53
-msgid "Administrative Cc"
-msgstr "Malkovich Cc"
-
-#: html/Elements/SelectDateRelation:35
-msgid "After"
-msgstr "Malkovich"
-
-#: etc/initialdata:363
-msgid "All Approvals Passed"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/EditCustomFields:94
-msgid "All Custom Fields"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/index.html:52
-msgid "All Queues"
-msgstr "Malkovich"
-
-#: html/Elements/Tabs:58
-msgid "Approval"
-msgstr "Malkovich"
-
-#: html/Approvals/Display.html:45 html/Approvals/Elements/ShowDependency:41 html/Approvals/index.html:64
-#. ($Ticket->Id, $Ticket->Subject)
-#. ($ticket->id, $msg)
-#. ($link->BaseObj->Id, $link->BaseObj->Subject)
-msgid "Approval #%1: %2"
-msgstr "Malkovich #%1: %2"
-
-#: html/Approvals/index.html:53
-#. ($ticket->Id)
-msgid "Approval #%1: Notes not recorded due to a system error"
-msgstr "Malkovich #%1: Malkovich Malkovich to a Malkovich"
-
-#: html/Approvals/index.html:51
-#. ($ticket->Id)
-msgid "Approval #%1: Notes recorded"
-msgstr "Malkovich #%1: Malkovich"
-
-#: etc/initialdata:351
-msgid "Approval Passed"
-msgstr "Malkovich"
-
-#: etc/initialdata:374
-msgid "Approval Rejected"
-msgstr "Malkovich Malkovich"
-
-#: html/Approvals/Elements/Approve:43
-msgid "Approve"
-msgstr "Malkovich"
-
-#: etc/initialdata:504
-msgid "Approver's notes: %1"
-msgstr "Malkovich's Malkovich: %1"
-
-#: lib/RT/Date.pm:414
-msgid "Apr."
-msgstr "Apr."
-
-#: html/Elements/SelectSortOrder:34 html/Search/Elements/DisplayOptions:52
-msgid "Ascending"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:134 html/SelfService/Update.html:47 html/Ticket/ModifyAll.html:82 html/Ticket/Update.html:81
-msgid "Attach"
-msgstr "Malkovich"
-
-#: html/SelfService/Create.html:64 html/Ticket/Create.html:109
-msgid "Attach file"
-msgstr "Malkovich"
-
-#: html/SelfService/Update.html:36 html/Ticket/Create.html:97 html/Ticket/Update.html:70
-msgid "Attached file"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:416
-msgid "Attachment created"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1251
-msgid "Attachment filename"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowAttachments:25
-msgid "Attachments"
-msgstr "Malkovich"
-
-#: lib/RT/Attributes_Overlay.pm:158
-msgid "Attribute Deleted"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Date.pm:418
-msgid "Aug."
-msgstr "Aug."
-
-#: NOT FOUND IN SOURCE
-msgid "AuthSystem"
-msgstr "Malkovich"
-
-#: etc/initialdata:221
-msgid "Autoreply"
-msgstr "Malkovich"
-
-#: etc/initialdata:72
-msgid "Autoreply To Requestors"
-msgstr "Malkovich To Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Bad data in %1"
-msgstr "Malkovich in %1"
-
-#: html/Admin/Elements/GroupTabs:38 html/Admin/Elements/QueueTabs:38 html/Admin/Elements/UserTabs:37 html/Ticket/Elements/Tabs:91 html/User/Elements/GroupTabs:37
-msgid "Basics"
-msgstr "Malkovich"
-
-#: html/Ticket/Update.html:64
-msgid "Bcc"
-msgstr "Bcc"
-
-#: html/Admin/Elements/EditScrip:73
-msgid "Be sure to save your changes"
-msgstr "Be sure to Malkovich Malkovich"
-
-#: html/Elements/SelectDateRelation:33 lib/RT/CurrentUser.pm:336
-msgid "Before"
-msgstr "Malkovich"
-
-#: etc/initialdata:217
-msgid "Blank"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/ShowHistory:38 html/Ticket/Elements/ShowHistory:44
-msgid "Brief headers"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:24 html/Search/Bulk.html:25
-msgid "Bulk ticket update"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:1533
-msgid "Can not modify system users"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:68
-msgid "Can this principal see this queue"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:211
-msgid "Can't add a custom field value without a name"
-msgstr "Can't add a Malkovich Malkovich Malkovich a name"
-
-#: lib/RT/Link_Overlay.pm:126
-msgid "Can't link a ticket to itself"
-msgstr "Can't link a Malkovich to Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2716
-msgid "Can't merge into a merged ticket. You should never get this error"
-msgstr "Can't Malkovich a Malkovich. Malkovich Malkovich Malkovich"
-
-#: lib/RT/Record.pm:1060 lib/RT/Record.pm:1138
-msgid "Can't specifiy both base and target"
-msgstr "Can't Malkovich Malkovich and Malkovich"
-
-#: html/autohandler:132
-#. ($msg)
-msgid "Cannot create user: %1"
-msgstr "Malkovich Malkovich: %1"
-
-#: etc/initialdata:50 html/Admin/Queues/People.html:43 html/SelfService/Create.html:48 html/Ticket/Create.html:63 html/Ticket/Elements/EditPeople:50 html/Ticket/Elements/ShowPeople:34 html/Ticket/Update.html:59 lib/RT/ACE_Overlay.pm:87
-msgid "Cc"
-msgstr "Cc"
-
-#: html/SelfService/Prefs.html:30
-msgid "Change password"
-msgstr "Malkovich"
-
-#: html/SelfService/Update.html:39 html/Ticket/Create.html:100 html/Ticket/Update.html:73
-msgid "Check box to delete"
-msgstr "Malkovich to Malkovich"
-
-#: html/Admin/Elements/SelectRights:30
-msgid "Check box to revoke right"
-msgstr "Malkovich to Malkovich"
-
-#: html/Elements/EditLinks:121 html/Elements/EditLinks:63 html/Elements/ShowLinks:56 html/Ticket/Create.html:183 html/Ticket/Elements/BulkLinks:42
-msgid "Children"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:129 html/User/Prefs.html:119
-msgid "City"
-msgstr "City"
-
-#: html/Ticket/Elements/ShowDates:47
-msgid "Closed"
-msgstr "Malkovich"
-
-#: html/SelfService/Closed.html:24
-msgid "Closed Tickets"
-msgstr "Malkovich"
-
-#: html/SelfService/Elements/Tabs:44
-msgid "Closed tickets"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/ShowTransaction:152 html/Ticket/Elements/Tabs:154
-msgid "Comment"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/Modify.html:57
-msgid "Comment Address"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:88
-msgid "Comment on tickets"
-msgstr "Malkovich on Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:88
-msgid "CommentOnTicket"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Comments"
-msgstr "Malkovich"
-
-#: html/Ticket/ModifyAll.html:69 html/Ticket/Update.html:51
-msgid "Comments (Not sent to requestors)"
-msgstr "Malkovich (Malkovich to Malkovich)"
-
-#: html/Search/Bulk.html:129
-msgid "Comments (not sent to requestors)"
-msgstr "Malkovich (Malkovich to Malkovich)"
-
-#: NOT FOUND IN SOURCE
-msgid "Comments about %1"
-msgstr "Malkovich %1"
-
-#: html/Admin/Users/Modify.html:182 html/Ticket/Elements/ShowRequestor:45
-msgid "Comments about this user"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:537
-msgid "Comments added"
-msgstr "Malkovich"
-
-#: lib/RT/Action/Generic.pm:149
-msgid "Commit Stubbed"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrip:41
-msgid "Condition"
-msgstr "Malkovich"
-
-#: bin/rt-crontool:105
-msgid "Condition matches..."
-msgstr "Malkovich Malkovich..."
-
-#: lib/RT/Scrip_Overlay.pm:164
-msgid "Condition not found"
-msgstr "Malkovich Malkovich"
-
-#: html/Elements/Tabs:52
-msgid "Configuration"
-msgstr "Malkovich"
-
-#: html/SelfService/Prefs.html:32
-msgid "Confirm"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "ContactInfoSystem"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/ModifyTemplate:43 html/Elements/SelectAttachmentField:26 html/Ticket/ModifyAll.html:86
-msgid "Content"
-msgstr "Malkovich"
-
-#: etc/initialdata:286
-msgid "Correspondence"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Correspondence Address"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:533
-msgid "Correspondence added"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3471
-msgid "Could not add new custom field value for ticket. "
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich. "
-
-#: lib/RT/Ticket_Overlay.pm:2967 lib/RT/Ticket_Overlay.pm:2975 lib/RT/Ticket_Overlay.pm:2992
-msgid "Could not change owner. "
-msgstr "Malkovich Malkovich. "
-
-#: html/Admin/Elements/EditCustomField:84 html/Admin/Elements/EditCustomFields:164
-#. ($msg)
-msgid "Could not create CustomField"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/User/Groups/Modify.html:76 lib/RT/Group_Overlay.pm:474 lib/RT/Group_Overlay.pm:481
-msgid "Could not create group"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Global/Template.html:74 html/Admin/Queues/Template.html:71
-#. ($msg)
-msgid "Could not create template: %1"
-msgstr "Malkovich Malkovich: %1"
-
-#: lib/RT/Ticket_Overlay.pm:1185 lib/RT/Ticket_Overlay.pm:364
-msgid "Could not create ticket. Queue not set"
-msgstr "Malkovich Malkovich. Malkovich"
-
-#: lib/RT/User_Overlay.pm:226 lib/RT/User_Overlay.pm:240 lib/RT/User_Overlay.pm:249 lib/RT/User_Overlay.pm:258 lib/RT/User_Overlay.pm:267 lib/RT/User_Overlay.pm:281 lib/RT/User_Overlay.pm:291 lib/RT/User_Overlay.pm:462
-msgid "Could not create user"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:662 lib/RT/Ticket_Overlay.pm:1515
-msgid "Could not find or create that user"
-msgstr "Malkovich or Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:723 lib/RT/Ticket_Overlay.pm:1596
-msgid "Could not find that principal"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Groups/Members.html:87 html/User/Groups/Members.html:89 html/User/Groups/Modify.html:81
-msgid "Could not load group"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:682
-#. ($args{'Type'})
-msgid "Could not make that principal a %1 for this queue"
-msgstr "Malkovich Malkovich Malkovich a %1 Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1536
-#. ($self->loc($args{'Type'}))
-msgid "Could not make that principal a %1 for this ticket"
-msgstr "Malkovich Malkovich Malkovich a %1 Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:781
-#. ($args{'Type'})
-msgid "Could not remove that principal as a %1 for this queue"
-msgstr "Malkovich Malkovich Malkovich as a %1 Malkovich"
-
-#: lib/RT/Group_Overlay.pm:977
-msgid "Couldn't add member to group"
-msgstr "Couldn't Malkovich to Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3481 lib/RT/Ticket_Overlay.pm:3537
-#. ($Msg)
-msgid "Couldn't create a transaction: %1"
-msgstr "Couldn't Malkovich a Malkovich: %1"
-
-#: lib/RT/Record.pm:748
-msgid "Couldn't find row"
-msgstr "Couldn't Malkovich"
-
-#: lib/RT/Group_Overlay.pm:951
-msgid "Couldn't find that principal"
-msgstr "Couldn't Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:245
-msgid "Couldn't find that value"
-msgstr "Couldn't Malkovich"
-
-#: lib/RT/CurrentUser.pm:123
-#. ($self->Id)
-msgid "Couldn't load %1 from the users database.\\n"
-msgstr "Couldn't load %1 from the Malkovich.\\n"
-
-#: html/Admin/Groups/GroupRights.html:87 html/Admin/Groups/UserRights.html:74
-#. ($id)
-msgid "Couldn't load group %1"
-msgstr "Couldn't Malkovich %1"
-
-#: lib/RT/Link_Overlay.pm:169 lib/RT/Link_Overlay.pm:178 lib/RT/Link_Overlay.pm:205
-msgid "Couldn't load link"
-msgstr "Couldn't Malkovich"
-
-#: html/Admin/Elements/EditCustomFields:145 html/Admin/Queues/CustomFields.html:35 html/Admin/Queues/People.html:120
-#. ($id)
-msgid "Couldn't load queue"
-msgstr "Couldn't Malkovich"
-
-#: html/Admin/Queues/GroupRights.html:100 html/Admin/Queues/UserRights.html:71
-#. ($id)
-msgid "Couldn't load queue %1"
-msgstr "Couldn't Malkovich %1"
-
-#: NOT FOUND IN SOURCE
-msgid "Couldn't load that user (%1)"
-msgstr "Couldn't Malkovich (%1)"
-
-#: html/SelfService/Display.html:116
-#. ($id)
-msgid "Couldn't load ticket '%1'"
-msgstr "Couldn't Malkovich '%1'"
-
-#: html/Admin/Users/Modify.html:146 html/User/Prefs.html:131
-msgid "Country"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/CreateUserCalled:25 html/Admin/Elements/EditCustomField:62 html/Admin/Elements/EditScrip:110 html/Admin/Groups/Modify.html:55 html/Admin/Queues/Template.html:44 html/Elements/QuickCreate:23 html/Ticket/Create.html:134 html/Ticket/Create.html:195 html/User/Groups/Modify.html:55
-msgid "Create"
-msgstr "Malkovich"
-
-#: etc/initialdata:135
-msgid "Create Tickets"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:74
-msgid "Create a CustomField"
-msgstr "Malkovich a Malkovich"
-
-#: html/Admin/Queues/CustomField.html:47
-#. ($QueueObj->Name())
-msgid "Create a CustomField for queue %1"
-msgstr "Malkovich a Malkovich Malkovich %1"
-
-#: html/Admin/Global/CustomField.html:47
-msgid "Create a CustomField which applies to all queues"
-msgstr "Malkovich a Malkovich Malkovich to Malkovich"
-
-#: html/Admin/Groups/Modify.html:66 html/Admin/Groups/Modify.html:92
-msgid "Create a new group"
-msgstr "Malkovich a Malkovich"
-
-#: html/User/Groups/Modify.html:66 html/User/Groups/Modify.html:91
-msgid "Create a new personal group"
-msgstr "Malkovich a Malkovich Malkovich"
-
-#: html/Ticket/Create.html:24 html/Ticket/Create.html:27 html/Ticket/Create.html:35
-msgid "Create a new ticket"
-msgstr "Malkovich a Malkovich"
-
-#: html/Admin/Users/Modify.html:211 html/Admin/Users/Modify.html:268
-msgid "Create a new user"
-msgstr "Malkovich a Malkovich"
-
-#: html/Admin/Queues/Modify.html:103
-msgid "Create a queue"
-msgstr "Malkovich a Malkovich"
-
-#: html/Admin/Queues/Scrip.html:58
-#. ($QueueObj->Name)
-msgid "Create a scrip for queue %1"
-msgstr "Malkovich a Malkovich %1"
-
-#: html/Admin/Global/Template.html:68 html/Admin/Queues/Template.html:64
-msgid "Create a template"
-msgstr "Malkovich a Malkovich"
-
-#: html/SelfService/Create.html:24
-msgid "Create a ticket"
-msgstr "Malkovich a Malkovich"
-
-#: etc/initialdata:137
-msgid "Create new tickets based on this scrip's template"
-msgstr "Malkovich Malkovich on Malkovich's Malkovich"
-
-#: html/SelfService/Create.html:77
-msgid "Create ticket"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:86
-msgid "Create tickets in this queue"
-msgstr "Malkovich in Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:73
-msgid "Create, delete and modify custom fields"
-msgstr "Malkovich, Malkovich and Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:69
-msgid "Create, delete and modify queues"
-msgstr "Malkovich, Malkovich and Malkovich"
-
-#: lib/RT/System.pm:58
-msgid "Create, delete and modify the members of personal groups"
-msgstr "Malkovich, Malkovich and Malkovich the Malkovich of Malkovich"
-
-#: lib/RT/System.pm:59
-msgid "Create, delete and modify users"
-msgstr "Malkovich, Malkovich and Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:86
-msgid "CreateTicket"
-msgstr "Malkovich"
-
-#: html/Elements/SelectDateType:25 html/Ticket/Elements/ShowDates:27 lib/RT/Ticket_Overlay.pm:1279
-msgid "Created"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:87
-#. ($CustomFieldObj->Name())
-msgid "Created CustomField %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Elements/EditLinks:27
-msgid "Current Links"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/EditScrips:29
-msgid "Current Scrips"
-msgstr "Malkovich"
-
-#: html/Admin/Groups/Members.html:38 html/User/Groups/Members.html:41
-msgid "Current members"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectRights:28
-msgid "Current rights"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Current search criteria"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/People.html:40 html/Ticket/Elements/EditPeople:44
-msgid "Current watchers"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Global/CustomField.html:54
-#. ($CustomField)
-msgid "Custom Field #%1"
-msgstr "Malkovich #%1"
-
-#: html/Admin/Elements/QueueTabs:52 html/Admin/Elements/SystemTabs:39 html/Admin/Global/index.html:49 html/Ticket/Elements/ShowSummary:35
-msgid "Custom Fields"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrip:101
-msgid "Custom action cleanup code"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Elements/EditScrip:93
-msgid "Custom action preparation code"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Elements/EditScrip:85
-msgid "Custom condition"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1693
-#. ($CF->Name , $args{OPERATOR} , $args{VALUE})
-msgid "Custom field %1 %2 %3"
-msgstr "Malkovich %1 %2 %3"
-
-#: lib/RT/Tickets_Overlay.pm:1688
-#. ($CF->Name)
-msgid "Custom field %1 has a value."
-msgstr "Malkovich %1 has a Malkovich."
-
-#: lib/RT/Tickets_Overlay.pm:1685
-#. ($CF->Name)
-msgid "Custom field %1 has no value."
-msgstr "Malkovich %1 has no Malkovich."
-
-#: lib/RT/Ticket_Overlay.pm:3373
-#. ($args{'Field'})
-msgid "Custom field %1 not found"
-msgstr "Malkovich %1 Malkovich"
-
-#: html/Admin/Elements/EditCustomFields:195
-msgid "Custom field deleted"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3523
-msgid "Custom field not found"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:355
-#. ($args{'Content'}, $self->Name)
-msgid "Custom field value %1 could not be found for custom field %2"
-msgstr "Malkovich Malkovich %1 Malkovich be Malkovich Malkovich %2"
-
-#: lib/RT/CustomField_Overlay.pm:255
-msgid "Custom field value could not be deleted"
-msgstr "Malkovich Malkovich Malkovich be Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:361
-msgid "Custom field value could not be found"
-msgstr "Malkovich Malkovich Malkovich be Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:253 lib/RT/CustomField_Overlay.pm:363
-msgid "Custom field value deleted"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:541
-msgid "CustomField"
-msgstr "Malkovich"
-
-#: html/SelfService/Display.html:38 html/Ticket/Create.html:160 html/Ticket/Elements/ShowSummary:54 html/Ticket/Elements/Tabs:94 html/Ticket/ModifyAll.html:43
-msgid "Dates"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:422
-msgid "Dec."
-msgstr "Dec."
-
-#: etc/initialdata:222
-msgid "Default Autoresponse template"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: etc/initialdata:296
-msgid "Default admin comment template"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: etc/initialdata:287
-msgid "Default correspondence template"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: etc/initialdata:253
-msgid "Default transaction template"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:519
-#. ($type, $self->Field, $self->OldValue, $self->NewValue)
-msgid "Default: %1/%2 changed from %3 to %4"
-msgstr "Malkovich: %1/%2 Malkovich %3 to %4"
-
-#: html/User/Delegation.html:24 html/User/Delegation.html:27
-msgid "Delegate rights"
-msgstr "Malkovich"
-
-#: lib/RT/System.pm:62
-msgid "Delegate specific rights which have been granted to you."
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich to you."
-
-#: lib/RT/System.pm:62
-msgid "DelegateRights"
-msgstr "Malkovich"
-
-#: html/User/Elements/Tabs:37
-msgid "Delegation"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrips:53 html/Search/Elements/EditFormat:66 html/Search/Elements/EditSearches:15
-msgid "Delete"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrips:52
-msgid "Delete selected scrips"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:91
-msgid "Delete tickets"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:91
-msgid "DeleteTicket"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:162
-msgid "Deleting this object could break referential integrity"
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:329
-msgid "Deleting this object would break referential integrity"
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:478
-msgid "Deleting this object would violate referential integrity"
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich Malkovich"
-
-#: html/Approvals/Elements/Approve:44
-msgid "Deny"
-msgstr "Deny"
-
-#: html/Elements/EditLinks:113 html/Elements/EditLinks:44 html/Elements/ShowLinks:36 html/Ticket/Create.html:181 html/Ticket/Elements/BulkLinks:34 html/Ticket/Elements/ShowDependencies:31
-msgid "Depended on by"
-msgstr "Malkovich on by"
-
-#: lib/RT/Transaction_Overlay.pm:621
-#. ($value)
-msgid "Dependency by %1 added"
-msgstr "Malkovich by %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:661
-#. ($value)
-msgid "Dependency by %1 deleted"
-msgstr "Malkovich by %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:618
-#. ($value)
-msgid "Dependency on %1 added"
-msgstr "Malkovich on %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:658
-#. ($value)
-msgid "Dependency on %1 deleted"
-msgstr "Malkovich on %1 Malkovich"
-
-#: html/Elements/EditLinks:109 html/Elements/EditLinks:35 html/Elements/SelectLinkType:26 html/Elements/ShowLinks:26 html/Ticket/Create.html:180 html/Ticket/Elements/BulkLinks:30 html/Ticket/Elements/ShowDependencies:24
-msgid "Depends on"
-msgstr "Malkovich on"
-
-#: html/Elements/SelectSortOrder:34 html/Search/Elements/DisplayOptions:57
-msgid "Descending"
-msgstr "Malkovich"
-
-#: html/SelfService/Create.html:72 html/Ticket/Create.html:118
-msgid "Describe the issue below"
-msgstr "Malkovich the Malkovich"
-
-#: html/Admin/Elements/AddCustomFieldValue:35 html/Admin/Elements/EditCustomField:38 html/Admin/Elements/EditScrip:34 html/Admin/Elements/ModifyTemplate:35 html/Admin/Groups/Modify.html:48 html/Admin/Queues/Modify.html:47 html/Elements/SelectGroups:26 html/Search/Elements/EditSearches:8 html/User/Groups/Modify.html:48
-msgid "Description"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/Tabs:86
-msgid "Display"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:70
-msgid "Display Access Control List"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:76
-msgid "Display Scrip templates for this queue"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:79
-msgid "Display Scrips for this queue"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowHistory:34
-msgid "Display mode"
-msgstr "Malkovich"
-
-#: lib/RT/System.pm:53
-msgid "Do anything and everything"
-msgstr "Do Malkovich and Malkovich"
-
-#: html/Elements/Refresh:29
-msgid "Don't refresh this page."
-msgstr "Don't Malkovich Malkovich."
-
-#: NOT FOUND IN SOURCE
-msgid "Don't show search results"
-msgstr "Don't Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowTransactionAttachments:60
-msgid "Download"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Download all the tickets as a tab delimited file"
-msgstr "Malkovich the Malkovich as a Malkovich Malkovich"
-
-#: html/Elements/SelectDateType:31 html/Ticket/Create.html:166 html/Ticket/Elements/EditDates:44 html/Ticket/Elements/ShowDates:43 lib/RT/Ticket_Overlay.pm:1283
-msgid "Due"
-msgstr "Due"
-
-#: NOT FOUND IN SOURCE
-msgid "ERROR: Couldn't load ticket '%1': %2.\\n"
-msgstr "MALKOVICH: Couldn't Malkovich '%1': %2.\\n"
-
-#: html/Admin/Queues/CustomFields.html:45
-#. ($Queue->Name)
-msgid "Edit Custom Fields for %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Search/Bulk.html:141 html/Ticket/ModifyLinks.html:35
-msgid "Edit Links"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/Templates.html:41
-#. ($QueueObj->Name)
-msgid "Edit Templates for queue %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Admin/Global/index.html:45
-msgid "Edit system templates"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/Modify.html:118
-#. ($QueueObj->Name)
-msgid "Editing Configuration for queue %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: NOT FOUND IN SOURCE
-msgid "Editing Configuration for user %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: html/Admin/Elements/EditCustomField:90
-#. ($CustomFieldObj->Name())
-msgid "Editing CustomField %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Admin/Groups/Members.html:31
-#. ($Group->Name)
-msgid "Editing membership for group %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: html/User/Groups/Members.html:128
-#. ($Group->Name)
-msgid "Editing membership for personal group %1"
-msgstr "Malkovich Malkovich Malkovich Malkovich %1"
-
-#: lib/RT/Record.pm:1075 lib/RT/Record.pm:1152
-msgid "Either base or target must be specified"
-msgstr "Malkovich or Malkovich be Malkovich"
-
-#: html/Admin/Users/Modify.html:52 html/Elements/SelectUsers:26 html/Ticket/Elements/AddWatchers:55 html/User/Prefs.html:43
-msgid "Email"
-msgstr "Malkovich"
-
-#: lib/RT/User_Overlay.pm:206
-msgid "Email address in use"
-msgstr "Malkovich in use"
-
-#: NOT FOUND IN SOURCE
-msgid "EmailAddress"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "EmailEncoding"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:50
-msgid "Enabled (Unchecking this box disables this custom field)"
-msgstr "Malkovich (Malkovich Malkovich Malkovich Malkovich Malkovich)"
-
-#: html/Admin/Groups/Modify.html:52 html/User/Groups/Modify.html:52
-msgid "Enabled (Unchecking this box disables this group)"
-msgstr "Malkovich (Malkovich Malkovich Malkovich Malkovich)"
-
-#: html/Admin/Queues/Modify.html:83
-msgid "Enabled (Unchecking this box disables this queue)"
-msgstr "Malkovich (Malkovich Malkovich Malkovich Malkovich)"
-
-#: html/Admin/Elements/EditCustomFields:97
-msgid "Enabled Custom Fields"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/index.html:55
-msgid "Enabled Queues"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:106 html/Admin/Groups/Modify.html:116 html/Admin/Queues/Modify.html:140 html/Admin/Users/Modify.html:308 html/User/Groups/Modify.html:116
-#. (loc_fuzzy($msg))
-msgid "Enabled status %1"
-msgstr "Malkovich %1"
-
-#: lib/RT/CustomField_Overlay.pm:433
-msgid "Enter multiple values"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:430
-msgid "Enter one value"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:142
-msgid "Enter tickets or URIs to link tickets to. Seperate multiple entries with spaces."
-msgstr "Malkovich or URIs to Malkovich to. Malkovich Malkovich Malkovich Malkovich."
-
-#: html/Elements/Login:39 html/SelfService/Error.html:24 html/SelfService/Error.html:25
-msgid "Error"
-msgstr "Error"
-
-#: lib/RT/Queue_Overlay.pm:593
-msgid "Error in parameters to Queue->AddWatcher"
-msgstr "Malkovich in Malkovich to Malkovich->Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Error in parameters to Queue->DelWatcher"
-msgstr "Malkovich in Malkovich to Malkovich->Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1468
-msgid "Error in parameters to Ticket->AddWatcher"
-msgstr "Malkovich in Malkovich to Malkovich->Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Error in parameters to Ticket->DelWatcher"
-msgstr "Malkovich in Malkovich to Malkovich->Malkovich"
-
-#: etc/initialdata:20
-msgid "Everyone"
-msgstr "Malkovich"
-
-#: bin/rt-crontool:190
-msgid "Example:"
-msgstr "Malkovich:"
-
-#: NOT FOUND IN SOURCE
-msgid "ExternalAuthId"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "ExternalContactInfoId"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Users/Modify.html:72
-msgid "Extra info"
-msgstr "Malkovich"
-
-#: lib/RT/User_Overlay.pm:342
-msgid "Failed to find 'Privileged' users pseudogroup."
-msgstr "Malkovich to find 'Malkovich' Malkovich Malkovich."
-
-#: lib/RT/User_Overlay.pm:349
-msgid "Failed to find 'Unprivileged' users pseudogroup"
-msgstr "Malkovich to find 'Malkovich' Malkovich Malkovich"
-
-#: bin/rt-crontool:134
-#. ($modname, $@)
-msgid "Failed to load module %1. (%2)"
-msgstr "Malkovich to Malkovich %1. (%2)"
-
-#: lib/RT/Date.pm:412
-msgid "Feb."
-msgstr "Feb."
-
-#: html/Search/Elements/PickBasics:60 html/Ticket/Create.html:154 html/Ticket/Elements/EditBasics:57 lib/RT/Tickets_Overlay.pm:1153
-msgid "Final Priority"
-msgstr "Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1274
-msgid "FinalPriority"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/People.html:60 html/Ticket/Elements/EditPeople:33
-msgid "Find group whose"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Queues/People.html:56 html/Admin/Users/index.html:45 html/Ticket/Elements/EditPeople:29
-msgid "Find people whose"
-msgstr "Malkovich Malkovich"
-
-#: html/Search/Results.html:72
-msgid "Find tickets"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/Tabs:59
-msgid "First"
-msgstr "Malkovich"
-
-#: docs/design_docs/string-extraction-guide.txt:33 lib/RT/StyleGuide.pod:746
-msgid "Foo Bar Baz"
-msgstr "Malkovich"
-
-#: docs/design_docs/string-extraction-guide.txt:24 lib/RT/StyleGuide.pod:737
-msgid "Foo!"
-msgstr "Foo!"
-
-#: html/Search/Bulk.html:84
-msgid "Force change"
-msgstr "Malkovich"
-
-#: html/Search/Results.html:70
-#. ($ticketcount)
-msgid "Found %quant(%1,ticket)"
-msgstr "Malkovich %quant(%1,Malkovich)"
-
-#: lib/RT/Record.pm:750
-msgid "Found Object"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "FreeformContactInfo"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:37
-msgid "FreeformMultiple"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:36
-msgid "FreeformSingle"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:392
-msgid "Fri."
-msgstr "Fri."
-
-#: html/Ticket/Elements/ShowHistory:40 html/Ticket/Elements/ShowHistory:50
-msgid "Full headers"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:587
-#. ($New->Name)
-msgid "Given to %1"
-msgstr "Malkovich to %1"
-
-#: html/Admin/Elements/Tabs:40 html/Admin/index.html:37
-msgid "Global"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectTemplate:37
-#. (loc($Template->Name))
-msgid "Global template: %1"
-msgstr "Malkovich: %1"
-
-#: html/Tools/Offline.html:69
-msgid "Go"
-msgstr "Go"
-
-#: html/Admin/Elements/EditCustomFields:73 html/Admin/Groups/index.html:39 html/Admin/Queues/People.html:58 html/Admin/Queues/People.html:62 html/Admin/Queues/index.html:43 html/Admin/Users/index.html:48 html/Ticket/Elements/EditPeople:31 html/Ticket/Elements/EditPeople:35 html/index.html:69
-msgid "Go!"
-msgstr "Go!"
-
-#: html/Elements/GotoTicket:24 html/SelfService/Elements/GotoTicket:24
-msgid "Goto ticket"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/AddWatchers:45 html/Ticket/Elements/ShowGroupMembers:33 html/User/Elements/DelegateRights:77
-msgid "Group"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/GroupTabs:44 html/Admin/Elements/QueueTabs:56 html/Admin/Elements/SystemTabs:43 html/Admin/Global/index.html:54
-msgid "Group Rights"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:957
-msgid "Group already has member"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Groups/Modify.html:76
-#. ($create_msg)
-msgid "Group could not be created: %1"
-msgstr "Malkovich be Malkovich: %1"
-
-#: lib/RT/Group_Overlay.pm:497
-msgid "Group created"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:1129
-msgid "Group has no such member"
-msgstr "Malkovich no Malkovich"
-
-#: lib/RT/Group_Overlay.pm:937 lib/RT/Queue_Overlay.pm:669 lib/RT/Queue_Overlay.pm:729 lib/RT/Ticket_Overlay.pm:1522 lib/RT/Ticket_Overlay.pm:1602
-msgid "Group not found"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectNewGroupMembers:34 html/Admin/Elements/Tabs:34 html/Admin/Groups/Members.html:63 html/Admin/Queues/People.html:82 html/Admin/index.html:31 html/User/Groups/Members.html:66
-msgid "Groups"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:963
-msgid "Groups can't be members of their members"
-msgstr "Malkovich can't be Malkovich of Malkovich"
-
-#: lib/RT/Interface/CLI.pm:72 lib/RT/Interface/CLI.pm:72
-msgid "Hello!"
-msgstr "Malkovich!"
-
-#: docs/design_docs/string-extraction-guide.txt:40 lib/RT/StyleGuide.pod:753
-#. ($name)
-msgid "Hello, %1"
-msgstr "Malkovich, %1"
-
-#: html/Ticket/Elements/ShowHistory:29 html/Ticket/Elements/Tabs:89
-msgid "History"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "HomePhone"
-msgstr "Malkovich"
-
-#: html/Elements/Tabs:43
-msgid "Homepage"
-msgstr "Malkovich"
-
-#: lib/RT/Base.pm:86
-#. (6)
-msgid "I have %quant(%1,concrete mixer)."
-msgstr "I have %quant(%1,Malkovich)."
-
-#: html/Search/Elements/PickBasics:104 html/Ticket/Elements/ShowBasics:26 lib/RT/Tickets_Overlay.pm:1080
-msgid "Id"
-msgstr "Id"
-
-#: html/Admin/Users/Modify.html:43 html/User/Prefs.html:38
-msgid "Identity"
-msgstr "Malkovich"
-
-#: etc/initialdata:429
-msgid "If an approval is rejected, reject the original and delete pending approvals"
-msgstr "If a Malkovich is Malkovich, Malkovich the Malkovich and Malkovich Malkovich"
-
-#: bin/rt-crontool:186
-msgid "If this tool were setgid, a hostile local user could use this tool to gain administrative access to RT."
-msgstr "If Malkovich Malkovich, a Malkovich Malkovich Malkovich Malkovich to Malkovich Malkovich Malkovich to RT."
-
-#: html/Admin/Queues/People.html:104 html/Ticket/Modify.html:38 html/Ticket/ModifyAll.html:93 html/Ticket/ModifyPeople.html:37
-msgid "If you've updated anything above, be sure to"
-msgstr "If you've Malkovich Malkovich, be sure to"
-
-#: lib/RT/Record.pm:742
-msgid "Illegal value for %1"
-msgstr "Malkovich Malkovich %1"
-
-#: lib/RT/Record.pm:745
-msgid "Immutable field"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomFields:72
-msgid "Include disabled custom fields in listing."
-msgstr "Malkovich Malkovich Malkovich in Malkovich."
-
-#: html/Admin/Queues/index.html:42
-msgid "Include disabled queues in listing."
-msgstr "Malkovich Malkovich in Malkovich."
-
-#: html/Admin/Users/index.html:46
-msgid "Include disabled users in search."
-msgstr "Malkovich Malkovich in Malkovich."
-
-#: html/Search/Elements/PickBasics:59 lib/RT/Tickets_Overlay.pm:1129
-msgid "Initial Priority"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1273 lib/RT/Ticket_Overlay.pm:1275
-msgid "InitialPriority"
-msgstr "Malkovich"
-
-#: lib/RT/ScripAction_Overlay.pm:97
-msgid "Input error"
-msgstr "Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3797
-msgid "Internal Error"
-msgstr "Malkovich"
-
-#: lib/RT/Record.pm:186
-#. ($id->{error_message})
-msgid "Internal Error: %1"
-msgstr "Malkovich: %1"
-
-#: lib/RT/Group_Overlay.pm:644
-msgid "Invalid Group Type"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Principal_Overlay.pm:127
-msgid "Invalid Right"
-msgstr "Malkovich"
-
-#: lib/RT/Record.pm:747
-msgid "Invalid data"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Invalid owner. Defaulting to 'nobody'."
-msgstr "Malkovich. Malkovich to 'Malkovich'."
-
-#: lib/RT/Scrip_Overlay.pm:133 lib/RT/Template_Overlay.pm:251
-msgid "Invalid queue"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:243 lib/RT/ACE_Overlay.pm:252 lib/RT/ACE_Overlay.pm:258 lib/RT/ACE_Overlay.pm:269 lib/RT/ACE_Overlay.pm:274
-msgid "Invalid right"
-msgstr "Malkovich"
-
-#: lib/RT/Record.pm:161
-#. ($key)
-msgid "Invalid value for %1"
-msgstr "Malkovich Malkovich %1"
-
-#: lib/RT/Ticket_Overlay.pm:3380
-msgid "Invalid value for custom field"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:385
-msgid "Invalid value for status"
-msgstr "Malkovich Malkovich"
-
-#: bin/rt-crontool:187
-msgid "It is incredibly important that nonprivileged users not be allowed to run this tool."
-msgstr "It is Malkovich Malkovich Malkovich Malkovich Malkovich be Malkovich to Malkovich."
-
-#: bin/rt-crontool:188
-msgid "It is suggested that you create a non-privileged unix user with the correct group membership and RT access to run this tool."
-msgstr "It is Malkovich Malkovich a non-Malkovich Malkovich the Malkovich Malkovich and RT Malkovich to Malkovich."
-
-#: bin/rt-crontool:159
-msgid "It takes several arguments:"
-msgstr "It Malkovich Malkovich:"
-
-#: lib/RT/Date.pm:411
-msgid "Jan."
-msgstr "Jan."
-
-#: lib/RT/Group_Overlay.pm:149
-msgid "Join or leave this group"
-msgstr "Join or Malkovich Malkovich"
-
-#: lib/RT/Date.pm:417
-msgid "Jul."
-msgstr "Jul."
-
-#: html/Ticket/Elements/Tabs:100
-msgid "Jumbo"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:416
-msgid "Jun."
-msgstr "Jun."
-
-#: NOT FOUND IN SOURCE
-msgid "Lang"
-msgstr "Lang"
-
-#: html/User/Prefs.html:54
-msgid "Language"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/Tabs:74
-msgid "Last"
-msgstr "Last"
-
-#: html/Ticket/Elements/EditDates:37 html/Ticket/Elements/ShowDates:39
-msgid "Last Contact"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Last Contact</a>"
-msgstr "Malkovich</a>"
-
-#: html/Elements/SelectDateType:28
-msgid "Last Contacted"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Last Notified"
-msgstr "Malkovich"
-
-#: html/Elements/SelectDateType:29
-msgid "Last Updated"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:82
-msgid "Let this user access RT"
-msgstr "Malkovich Malkovich RT"
-
-#: html/Admin/Users/Modify.html:86
-msgid "Let this user be granted rights"
-msgstr "Malkovich be Malkovich"
-
-#: lib/RT/Record.pm:1086
-msgid "Link already exists"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Record.pm:1100
-msgid "Link could not be created"
-msgstr "Malkovich be Malkovich"
-
-#: lib/RT/Record.pm:1106
-#. ($TransString)
-msgid "Link created (%1)"
-msgstr "Malkovich (%1)"
-
-#: lib/RT/Record.pm:1167
-#. ($TransString)
-msgid "Link deleted (%1)"
-msgstr "Malkovich (%1)"
-
-#: lib/RT/Record.pm:1173
-msgid "Link not found"
-msgstr "Malkovich"
-
-#: html/Ticket/ModifyLinks.html:24 html/Ticket/ModifyLinks.html:28
-#. ($Ticket->Id)
-msgid "Link ticket #%1"
-msgstr "Malkovich #%1"
-
-#: html/Ticket/Create.html:174 html/Ticket/Elements/ShowSummary:61 html/Ticket/Elements/Tabs:98 html/Ticket/ModifyAll.html:56
-msgid "Links"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:111 html/User/Prefs.html:104
-msgid "Location"
-msgstr "Malkovich"
-
-#: lib/RT.pm:184
-#. ($RT::LogDir)
-msgid "Log directory %1 not found or couldn't be written.\\n RT can't run."
-msgstr "Malkovich %1 Malkovich or couldn't be Malkovich.\\n RT can't run."
-
-#: html/Elements/Header:69
-#. ("<b>".$session{'CurrentUser'}->Name."</b>")
-msgid "Logged in as %1"
-msgstr "Malkovich in as %1"
-
-#: docs/design_docs/string-extraction-guide.txt:71 html/Elements/Login:35 html/Elements/Login:44 html/Elements/Login:54 lib/RT/StyleGuide.pod:777
-msgid "Login"
-msgstr "Malkovich"
-
-#: html/Elements/Header:66
-msgid "Logout"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:83
-msgid "Make Owner"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:107
-msgid "Make Status"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:115
-msgid "Make date Due"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:117
-msgid "Make date Resolved"
-msgstr "Malkovich Malkovich"
-
-#: html/Search/Bulk.html:111
-msgid "Make date Started"
-msgstr "Malkovich Malkovich"
-
-#: html/Search/Bulk.html:109
-msgid "Make date Starts"
-msgstr "Malkovich Malkovich"
-
-#: html/Search/Bulk.html:113
-msgid "Make date Told"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:103
-msgid "Make priority"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:105
-msgid "Make queue"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:101
-msgid "Make subject"
-msgstr "Malkovich"
-
-#: html/Admin/index.html:32
-msgid "Manage groups and group membership"
-msgstr "Malkovich and Malkovich Malkovich"
-
-#: html/Admin/index.html:38
-msgid "Manage properties and configuration which apply to all queues"
-msgstr "Malkovich Malkovich and Malkovich Malkovich to Malkovich"
-
-#: html/Admin/index.html:35
-msgid "Manage queues and queue-specific properties"
-msgstr "Malkovich and Malkovich-Malkovich Malkovich"
-
-#: html/Admin/index.html:29
-msgid "Manage users and passwords"
-msgstr "Malkovich and Malkovich"
-
-#: lib/RT/Date.pm:413
-msgid "Mar."
-msgstr "Mar."
-
-#: lib/RT/Date.pm:415
-msgid "May."
-msgstr "May."
-
-#: lib/RT/Transaction_Overlay.pm:634
-#. ($value)
-msgid "Member %1 added"
-msgstr "Malkovich %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:674
-#. ($value)
-msgid "Member %1 deleted"
-msgstr "Malkovich %1 Malkovich"
-
-#: lib/RT/Group_Overlay.pm:974
-msgid "Member added"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:1136
-msgid "Member deleted"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:1140
-msgid "Member not deleted"
-msgstr "Malkovich Malkovich"
-
-#: html/Elements/SelectLinkType:25
-msgid "Member of"
-msgstr "Malkovich of"
-
-#: html/Admin/Elements/GroupTabs:41 html/User/Elements/GroupTabs:41
-msgid "Members"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:631
-#. ($value)
-msgid "Membership in %1 added"
-msgstr "Malkovich in %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:671
-#. ($value)
-msgid "Membership in %1 deleted"
-msgstr "Malkovich in %1 Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2813
-msgid "Merge Successful"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2733
-msgid "Merge failed. Couldn't set EffectiveId"
-msgstr "Malkovich. Couldn't Malkovich"
-
-#: html/Elements/EditLinks:104 html/Ticket/Elements/BulkLinks:26
-msgid "Merge into"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:135 html/Ticket/Update.html:83
-msgid "Message"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Message body not shown because it is too large or is not plain text."
-msgstr "Malkovich Malkovich Malkovich it is Malkovich or is Malkovich."
-
-#: lib/RT/Ticket_Overlay.pm:2514
-msgid "Message could not be recorded"
-msgstr "Malkovich Malkovich be Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Message recipients"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2517
-msgid "Message recorded"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Record.pm:749
-msgid "Missing a primary key?: %1"
-msgstr "Malkovich a Malkovich?: %1"
-
-#: html/Admin/Users/Modify.html:166 html/User/Prefs.html:71
-msgid "Mobile"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "MobilePhone"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:71
-msgid "Modify Access Control List"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Global/CustomFields.html:43 html/Admin/Global/index.html:50
-msgid "Modify Custom Fields which apply to all queues"
-msgstr "Malkovich Malkovich Malkovich to Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:74
-msgid "Modify Scrip templates for this queue"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:77
-msgid "Modify Scrips for this queue"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Queues/CustomField.html:44
-#. ($QueueObj->Name())
-msgid "Modify a CustomField for queue %1"
-msgstr "Malkovich a Malkovich Malkovich %1"
-
-#: html/Admin/Global/CustomField.html:52
-msgid "Modify a CustomField which applies to all queues"
-msgstr "Malkovich a Malkovich Malkovich to Malkovich"
-
-#: html/Admin/Queues/Scrip.html:53
-#. ($QueueObj->Name)
-msgid "Modify a scrip for queue %1"
-msgstr "Malkovich a Malkovich %1"
-
-#: html/Admin/Global/Scrip.html:47
-msgid "Modify a scrip which applies to all queues"
-msgstr "Malkovich a Malkovich Malkovich to Malkovich"
-
-#: html/Ticket/ModifyDates.html:24 html/Ticket/ModifyDates.html:28
-#. ($TicketObj->Id)
-msgid "Modify dates for #%1"
-msgstr "Malkovich Malkovich #%1"
-
-#: html/Ticket/ModifyDates.html:34
-#. ($TicketObj->Id)
-msgid "Modify dates for ticket # %1"
-msgstr "Malkovich Malkovich # %1"
-
-#: html/Admin/Global/GroupRights.html:24 html/Admin/Global/GroupRights.html:27 html/Admin/Global/index.html:55
-msgid "Modify global group rights"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Global/GroupRights.html:32
-msgid "Modify global group rights."
-msgstr "Malkovich Malkovich Malkovich."
-
-#: html/Admin/Global/UserRights.html:24 html/Admin/Global/UserRights.html:27 html/Admin/Global/index.html:59
-msgid "Modify global user rights"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Global/UserRights.html:32
-msgid "Modify global user rights."
-msgstr "Malkovich Malkovich."
-
-#: lib/RT/Group_Overlay.pm:146
-msgid "Modify group metadata or delete group"
-msgstr "Malkovich Malkovich or Malkovich"
-
-#: html/Admin/Groups/GroupRights.html:24 html/Admin/Groups/GroupRights.html:28 html/Admin/Groups/GroupRights.html:34
-#. ($GroupObj->Name)
-msgid "Modify group rights for group %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: html/Admin/Queues/GroupRights.html:24 html/Admin/Queues/GroupRights.html:28
-#. ($QueueObj->Name)
-msgid "Modify group rights for queue %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: lib/RT/Group_Overlay.pm:148
-msgid "Modify membership roster for this group"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/System.pm:60
-msgid "Modify one's own RT account"
-msgstr "Malkovich's own RT Malkovich"
-
-#: html/Admin/Queues/People.html:24 html/Admin/Queues/People.html:28
-#. ($QueueObj->Name)
-msgid "Modify people related to queue %1"
-msgstr "Malkovich Malkovich to Malkovich %1"
-
-#: html/Ticket/ModifyPeople.html:24 html/Ticket/ModifyPeople.html:28 html/Ticket/ModifyPeople.html:34
-#. ($Ticket->id)
-#. ($Ticket->Id)
-msgid "Modify people related to ticket #%1"
-msgstr "Malkovich Malkovich to Malkovich #%1"
-
-#: html/Admin/Queues/Scrips.html:45
-#. ($QueueObj->Name)
-msgid "Modify scrips for queue %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Admin/Global/Scrips.html:43 html/Admin/Global/index.html:41
-msgid "Modify scrips which apply to all queues"
-msgstr "Malkovich Malkovich to Malkovich"
-
-#: html/Admin/Global/Template.html:24 html/Admin/Global/Template.html:29 html/Admin/Global/Template.html:80 html/Admin/Queues/Template.html:77
-#. (loc($TemplateObj->Name()))
-#. ($TemplateObj->id)
-msgid "Modify template %1"
-msgstr "Malkovich %1"
-
-#: html/Admin/Global/Templates.html:43
-msgid "Modify templates which apply to all queues"
-msgstr "Malkovich Malkovich Malkovich to Malkovich"
-
-#: html/Admin/Groups/Modify.html:86 html/User/Groups/Modify.html:85
-#. ($Group->Name)
-msgid "Modify the group %1"
-msgstr "Malkovich the Malkovich %1"
-
-#: lib/RT/Queue_Overlay.pm:72
-msgid "Modify the queue watchers"
-msgstr "Malkovich the Malkovich"
-
-#: html/Admin/Users/Modify.html:263
-#. ($UserObj->Name)
-msgid "Modify the user %1"
-msgstr "Malkovich the user %1"
-
-#: html/Ticket/ModifyAll.html:36
-#. ($Ticket->Id)
-msgid "Modify ticket # %1"
-msgstr "Malkovich # %1"
-
-#: html/Ticket/Modify.html:24 html/Ticket/Modify.html:27 html/Ticket/Modify.html:33
-#. ($TicketObj->Id)
-msgid "Modify ticket #%1"
-msgstr "Malkovich #%1"
-
-#: lib/RT/Queue_Overlay.pm:90
-msgid "Modify tickets"
-msgstr "Malkovich"
-
-#: html/Admin/Groups/UserRights.html:24 html/Admin/Groups/UserRights.html:28 html/Admin/Groups/UserRights.html:34
-#. ($GroupObj->Name)
-msgid "Modify user rights for group %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: html/Admin/Queues/UserRights.html:24 html/Admin/Queues/UserRights.html:28
-#. ($QueueObj->Name)
-msgid "Modify user rights for queue %1"
-msgstr "Malkovich Malkovich Malkovich %1"
-
-#: lib/RT/Queue_Overlay.pm:71
-msgid "ModifyACL"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:149
-msgid "ModifyOwnMembership"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:72
-msgid "ModifyQueueWatchers"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:77
-msgid "ModifyScrips"
-msgstr "Malkovich"
-
-#: lib/RT/System.pm:60
-msgid "ModifySelf"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:74
-msgid "ModifyTemplate"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:90
-msgid "ModifyTicket"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:388
-msgid "Mon."
-msgstr "Mon."
-
-#: html/Ticket/Elements/ShowRequestor:40
-#. ($name)
-msgid "More about %1"
-msgstr "Malkovich %1"
-
-#: html/Admin/Elements/EditCustomFields:60
-msgid "Move down"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectSingleOrMultiple:26
-msgid "Multiple"
-msgstr "Malkovich"
-
-#: lib/RT/User_Overlay.pm:197
-msgid "Must specify 'Name' attribute"
-msgstr "Malkovich 'Name' Malkovich"
-
-#: html/SelfService/Elements/MyRequests:48
-#. ($friendly_status)
-msgid "My %1 tickets"
-msgstr "My %1 Malkovich"
-
-#: html/Approvals/index.html:24 html/Approvals/index.html:25
-msgid "My approvals"
-msgstr "My Malkovich"
-
-#: html/Admin/Elements/AddCustomFieldValue:31 html/Admin/Elements/EditCustomField:33 html/Admin/Elements/ModifyTemplate:27 html/Admin/Groups/Modify.html:43 html/Elements/SelectGroups:25 html/Elements/SelectUsers:27 html/User/Groups/Modify.html:43
-msgid "Name"
-msgstr "Name"
-
-#: lib/RT/User_Overlay.pm:204
-msgid "Name in use"
-msgstr "Name in use"
-
-#: html/Ticket/Elements/ShowDates:52
-msgid "Never"
-msgstr "Malkovich"
-
-#: html/Elements/Quicksearch:29
-msgid "New"
-msgstr "New"
-
-#: html/Elements/EditLinks:93
-msgid "New Links"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Users/Modify.html:92 html/User/Prefs.html:87
-msgid "New Password"
-msgstr "Malkovich"
-
-#: etc/initialdata:332
-msgid "New Pending Approval"
-msgstr "Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "New Search"
-msgstr "Malkovich"
-
-#: html/Admin/Global/CustomField.html:40 html/Admin/Global/CustomFields.html:38 html/Admin/Queues/CustomField.html:51 html/Admin/Queues/CustomFields.html:40
-msgid "New custom field"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/GroupTabs:53 html/User/Elements/GroupTabs:51
-msgid "New group"
-msgstr "Malkovich"
-
-#: html/SelfService/Prefs.html:31
-msgid "New password"
-msgstr "Malkovich"
-
-#: lib/RT/User_Overlay.pm:773
-msgid "New password notification sent"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Elements/QueueTabs:69
-msgid "New queue"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectRights:41
-msgid "New rights"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Scrip.html:39 html/Admin/Global/Scrips.html:38 html/Admin/Queues/Scrip.html:42 html/Admin/Queues/Scrips.html:54
-msgid "New scrip"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Template.html:59 html/Admin/Global/Templates.html:38 html/Admin/Queues/Template.html:57 html/Admin/Queues/Templates.html:49
-msgid "New template"
-msgstr "Malkovich"
-
-#: html/SelfService/Elements/Tabs:47
-msgid "New ticket"
-msgstr "Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2700
-msgid "New ticket doesn't exist"
-msgstr "Malkovich doesn't Malkovich"
-
-#: html/Admin/Elements/UserTabs:50
-msgid "New user"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/CreateUserCalled:25
-msgid "New user called"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/People.html:54 html/Ticket/Elements/EditPeople:28
-msgid "New watchers"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "New window setting"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/Elements/Tabs:70
-msgid "Next"
-msgstr "Next"
-
-#: NOT FOUND IN SOURCE
-msgid "NickName"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:62 html/User/Prefs.html:50
-msgid "Nickname"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:89 html/Admin/Elements/EditCustomFields:103
-msgid "No CustomField"
-msgstr "No Malkovich"
-
-#: html/Admin/Groups/GroupRights.html:83 html/Admin/Groups/UserRights.html:70
-msgid "No Group defined"
-msgstr "No Malkovich"
-
-#: lib/RT/Tickets_Overlay_SQL.pm:452
-msgid "No Query"
-msgstr "No Malkovich"
-
-#: html/Admin/Queues/GroupRights.html:96 html/Admin/Queues/UserRights.html:67
-msgid "No Queue defined"
-msgstr "No Malkovich"
-
-#: bin/rt-crontool:52
-msgid "No RT user found. Please consult your RT administrator.\\n"
-msgstr "No RT Malkovich. Malkovich Malkovich RT Malkovich.\\n"
-
-#: html/Admin/Global/Template.html:78 html/Admin/Queues/Template.html:75
-msgid "No Template"
-msgstr "No Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "No Ticket specified. Aborting ticket "
-msgstr "No Malkovich Malkovich. Malkovich "
-
-#: html/Approvals/Elements/Approve:45
-msgid "No action"
-msgstr "No Malkovich"
-
-#: lib/RT/Record.pm:744
-msgid "No column specified"
-msgstr "No Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowRequestor:46
-msgid "No comment entered about this user"
-msgstr "No Malkovich Malkovich Malkovich"
-
-#: lib/RT/Action/Generic.pm:159 lib/RT/Condition/Generic.pm:175 lib/RT/Search/ActiveTicketsInQueue.pm:55 lib/RT/Search/Generic.pm:112
-#. (ref $self)
-msgid "No description for %1"
-msgstr "No Malkovich %1"
-
-#: lib/RT/Users_Overlay.pm:159
-msgid "No group specified"
-msgstr "No Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2475
-msgid "No message attached"
-msgstr "No Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:991
-msgid "No password set"
-msgstr "No Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:296
-msgid "No permission to create queues"
-msgstr "No Malkovich to Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "No permission to create tickets in the queue '%1'"
-msgstr "No Malkovich to Malkovich in the Malkovich '%1'"
-
-#: lib/RT/User_Overlay.pm:157
-msgid "No permission to create users"
-msgstr "No Malkovich to Malkovich"
-
-#: html/SelfService/Display.html:125
-msgid "No permission to display that ticket"
-msgstr "No Malkovich to Malkovich Malkovich"
-
-#: html/SelfService/Update.html:68
-msgid "No permission to view update ticket"
-msgstr "No Malkovich to Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:716 lib/RT/Ticket_Overlay.pm:1581
-msgid "No principal specified"
-msgstr "No Malkovich Malkovich"
-
-#: html/Admin/Queues/People.html:153 html/Admin/Queues/People.html:163
-msgid "No principals selected."
-msgstr "No Malkovich Malkovich."
-
-#: html/Admin/Queues/index.html:34
-msgid "No queues matching search criteria found."
-msgstr "No Malkovich Malkovich Malkovich Malkovich."
-
-#: html/Admin/Elements/SelectRights:81
-msgid "No rights found"
-msgstr "No Malkovich"
-
-#: html/Admin/Elements/SelectRights:32
-msgid "No rights granted."
-msgstr "No Malkovich."
-
-#: html/Search/Bulk.html:162
-msgid "No search to operate on."
-msgstr "No Malkovich to Malkovich on."
-
-#: lib/RT/Transaction_Overlay.pm:455 lib/RT/Transaction_Overlay.pm:493
-msgid "No transaction type specified"
-msgstr "No Malkovich Malkovich Malkovich"
-
-#: html/Admin/Users/index.html:35
-msgid "No users matching search criteria found."
-msgstr "No Malkovich Malkovich Malkovich Malkovich."
-
-#: NOT FOUND IN SOURCE
-msgid "No valid RT user found. RT cvs handler disengaged. Please consult your RT administrator.\\n"
-msgstr "No Malkovich RT Malkovich. RT Malkovich Malkovich. Malkovich Malkovich RT Malkovich.\\n"
-
-#: lib/RT/Record.pm:741
-msgid "No value sent to _Set!\\n"
-msgstr "No Malkovich to _Set!\\n"
-
-#: lib/RT/Record.pm:746
-msgid "Nonexistant field?"
-msgstr "Malkovich Malkovich?"
-
-#: html/Elements/Header:71
-msgid "Not logged in."
-msgstr "Malkovich in."
-
-#: lib/RT/Date.pm:369
-msgid "Not set"
-msgstr "Malkovich"
-
-#: html/NoAuth/Reminder.html:26
-msgid "Not yet implemented."
-msgstr "Malkovich Malkovich."
-
-#: html/Approvals/Elements/Approve:48
-msgid "Notes"
-msgstr "Malkovich"
-
-#: lib/RT/User_Overlay.pm:776
-msgid "Notification could not be sent"
-msgstr "Malkovich Malkovich be sent"
-
-#: etc/initialdata:101
-msgid "Notify AdminCcs"
-msgstr "Malkovich"
-
-#: etc/initialdata:97
-msgid "Notify AdminCcs as Comment"
-msgstr "Malkovich as Malkovich"
-
-#: etc/initialdata:128
-msgid "Notify Other Recipients"
-msgstr "Malkovich Malkovich"
-
-#: etc/initialdata:124
-msgid "Notify Other Recipients as Comment"
-msgstr "Malkovich Malkovich as Malkovich"
-
-#: etc/initialdata:85
-msgid "Notify Owner"
-msgstr "Malkovich"
-
-#: etc/initialdata:81
-msgid "Notify Owner as Comment"
-msgstr "Malkovich as Malkovich"
-
-#: etc/initialdata:376
-msgid "Notify Owner of their rejected ticket"
-msgstr "Malkovich of Malkovich Malkovich"
-
-#: etc/initialdata:365
-msgid "Notify Owner of their ticket has been approved by all approvers"
-msgstr "Malkovich of Malkovich Malkovich Malkovich by Malkovich"
-
-#: etc/initialdata:353
-msgid "Notify Owner of their ticket has been approved by some approver"
-msgstr "Malkovich of Malkovich Malkovich Malkovich by Malkovich"
-
-#: etc/initialdata:334
-msgid "Notify Owners and AdminCcs of new items pending their approval"
-msgstr "Malkovich and Malkovich of Malkovich Malkovich Malkovich"
-
-#: etc/initialdata:77
-msgid "Notify Requestors"
-msgstr "Malkovich Malkovich"
-
-#: etc/initialdata:111
-msgid "Notify Requestors and Ccs"
-msgstr "Malkovich Malkovich and Ccs"
-
-#: etc/initialdata:106
-msgid "Notify Requestors and Ccs as Comment"
-msgstr "Malkovich Malkovich and Ccs as Malkovich"
-
-#: etc/initialdata:120
-msgid "Notify Requestors, Ccs and AdminCcs"
-msgstr "Malkovich Malkovich, Ccs and Malkovich"
-
-#: etc/initialdata:116
-msgid "Notify Requestors, Ccs and AdminCcs as Comment"
-msgstr "Malkovich Malkovich, Ccs and Malkovich as Malkovich"
-
-#: lib/RT/Date.pm:421
-msgid "Nov."
-msgstr "Nov."
-
-#: lib/RT/Record.pm:200
-msgid "Object could not be created"
-msgstr "Malkovich Malkovich be Malkovich"
-
-#: lib/RT/Record.pm:219
-msgid "Object created"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:420
-msgid "Oct."
-msgstr "Oct."
-
-#: html/Elements/SelectDateRelation:34
-msgid "On"
-msgstr "On"
-
-#: etc/initialdata:163
-msgid "On Comment"
-msgstr "On Malkovich"
-
-#: etc/initialdata:156
-msgid "On Correspond"
-msgstr "On Malkovich"
-
-#: etc/initialdata:145
-msgid "On Create"
-msgstr "On Malkovich"
-
-#: etc/initialdata:184
-msgid "On Owner Change"
-msgstr "On Malkovich"
-
-#: etc/initialdata:192
-msgid "On Queue Change"
-msgstr "On Malkovich"
-
-#: etc/initialdata:198
-msgid "On Resolve"
-msgstr "On Malkovich"
-
-#: etc/initialdata:169
-msgid "On Status Change"
-msgstr "On Malkovich"
-
-#: etc/initialdata:150
-msgid "On Transaction"
-msgstr "On Malkovich"
-
-#: html/Approvals/Elements/PendingMyApproval:49
-#. ("<input size='15' value='".( $created_after->Unix >0 && $created_after->ISO)."' name='CreatedAfter'>")
-msgid "Only show approvals for requests created after %1"
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich %1"
-
-#: html/Approvals/Elements/PendingMyApproval:47
-#. ("<input size='15' value='".($created_before->Unix > 0 &&$created_before->ISO)."' name='CreatedBefore'>")
-msgid "Only show approvals for requests created before %1"
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich %1"
-
-#: html/Elements/Quicksearch:30
-msgid "Open"
-msgstr "Open"
-
-#: html/Ticket/Elements/Tabs:137
-msgid "Open it"
-msgstr "Open it"
-
-#: html/SelfService/Elements/Tabs:41
-msgid "Open tickets"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Open tickets (from listing) in a new window"
-msgstr "Malkovich (Malkovich) in a Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Open tickets (from listing) in another window"
-msgstr "Malkovich (Malkovich) in Malkovich"
-
-#: etc/initialdata:140
-msgid "Open tickets on correspondence"
-msgstr "Malkovich on Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Ordering and sorting"
-msgstr "Malkovich and Malkovich"
-
-#: html/Admin/Users/Modify.html:114 html/Elements/SelectUsers:28 html/User/Prefs.html:107
-msgid "Organization"
-msgstr "Malkovich"
-
-#: html/Approvals/Elements/Approve:32
-#. ($approving->Id, $approving->Subject)
-msgid "Originating ticket: #%1"
-msgstr "Malkovich Malkovich: #%1"
-
-#: html/Admin/Queues/Modify.html:68
-msgid "Over time, priority moves toward"
-msgstr "Malkovich, Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:89
-msgid "Own tickets"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:89
-msgid "OwnTicket"
-msgstr "Malkovich"
-
-#: etc/initialdata:38 html/Elements/QuickCreate:13 html/Search/Elements/PickBasics:114 html/SelfService/Elements/MyRequests:29 html/Ticket/Create.html:47 html/Ticket/Elements/EditPeople:42 html/Ticket/Elements/EditPeople:43 html/Ticket/Elements/ShowPeople:26 html/Ticket/Update.html:40 lib/RT/ACE_Overlay.pm:85 lib/RT/Tickets_Overlay.pm:1306
-msgid "Owner"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:575
-#. ($Old->Name , $New->Name)
-msgid "Owner forcibly changed from %1 to %2"
-msgstr "Malkovich Malkovich Malkovich %1 to %2"
-
-#: NOT FOUND IN SOURCE
-msgid "Owner is"
-msgstr "Malkovich is"
-
-#: html/Admin/Users/Modify.html:171 html/User/Prefs.html:75
-msgid "Pager"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "PagerPhone"
-msgstr "Malkovich"
-
-#: html/Elements/EditLinks:117 html/Elements/EditLinks:54 html/Elements/ShowLinks:46 html/Ticket/Create.html:182 html/Ticket/Elements/BulkLinks:38
-msgid "Parents"
-msgstr "Malkovich"
-
-#: html/Elements/Login:52 html/User/Prefs.html:83
-msgid "Password"
-msgstr "Malkovich"
-
-#: html/NoAuth/Reminder.html:24
-msgid "Password Reminder"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:185 lib/RT/User_Overlay.pm:994
-msgid "Password too short"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Users/Modify.html:316 html/User/Prefs.html:209
-#. (loc_fuzzy($msg))
-msgid "Password: %1"
-msgstr "Malkovich: %1"
-
-#: html/Admin/Users/Modify.html:318
-msgid "Passwords do not match."
-msgstr "Malkovich do Malkovich."
-
-#: html/User/Prefs.html:211
-msgid "Passwords do not match. Your password has not been changed"
-msgstr "Malkovich do Malkovich. Malkovich Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowSummary:44 html/Ticket/Elements/Tabs:97 html/Ticket/ModifyAll.html:50
-msgid "People"
-msgstr "Malkovich"
-
-#: etc/initialdata:133
-msgid "Perform a user-defined action"
-msgstr "Malkovich a user-Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:230 lib/RT/ACE_Overlay.pm:236 lib/RT/ACE_Overlay.pm:562 lib/RT/ACE_Overlay.pm:572 lib/RT/ACE_Overlay.pm:582 lib/RT/ACE_Overlay.pm:647 lib/RT/Attribute_Overlay.pm:135 lib/RT/Attribute_Overlay.pm:141 lib/RT/Attribute_Overlay.pm:379 lib/RT/Attribute_Overlay.pm:388 lib/RT/Attribute_Overlay.pm:401 lib/RT/CurrentUser.pm:103 lib/RT/CurrentUser.pm:94 lib/RT/CustomField_Overlay.pm:100 lib/RT/CustomField_Overlay.pm:207 lib/RT/CustomField_Overlay.pm:239 lib/RT/CustomField_Overlay.pm:517 lib/RT/CustomField_Overlay.pm:90 lib/RT/Group_Overlay.pm:1091 lib/RT/Group_Overlay.pm:1095 lib/RT/Group_Overlay.pm:1104 lib/RT/Group_Overlay.pm:1155 lib/RT/Group_Overlay.pm:1159 lib/RT/Group_Overlay.pm:1165 lib/RT/Group_Overlay.pm:426 lib/RT/Group_Overlay.pm:518 lib/RT/Group_Overlay.pm:596 lib/RT/Group_Overlay.pm:604 lib/RT/Group_Overlay.pm:701 lib/RT/Group_Overlay.pm:705 lib/RT/Group_Overlay.pm:711 lib/RT/Group_Overlay.pm:896 lib/RT/Group_Overlay.pm:900 lib/RT/Group_Overlay.pm:913 lib/RT/Queue_Overlay.pm:117 lib/RT/Queue_Overlay.pm:135 lib/RT/Queue_Overlay.pm:578 lib/RT/Queue_Overlay.pm:588 lib/RT/Queue_Overlay.pm:602 lib/RT/Queue_Overlay.pm:740 lib/RT/Queue_Overlay.pm:749 lib/RT/Queue_Overlay.pm:762 lib/RT/Queue_Overlay.pm:975 lib/RT/Scrip_Overlay.pm:125 lib/RT/Scrip_Overlay.pm:136 lib/RT/Scrip_Overlay.pm:201 lib/RT/Scrip_Overlay.pm:473 lib/RT/Template_Overlay.pm:284 lib/RT/Template_Overlay.pm:87 lib/RT/Template_Overlay.pm:93 lib/RT/Ticket_Overlay.pm:1453 lib/RT/Ticket_Overlay.pm:1463 lib/RT/Ticket_Overlay.pm:1477 lib/RT/Ticket_Overlay.pm:1614 lib/RT/Ticket_Overlay.pm:1624 lib/RT/Ticket_Overlay.pm:1638 lib/RT/Ticket_Overlay.pm:1755 lib/RT/Ticket_Overlay.pm:2075 lib/RT/Ticket_Overlay.pm:2213 lib/RT/Ticket_Overlay.pm:2381 lib/RT/Ticket_Overlay.pm:2428 lib/RT/Ticket_Overlay.pm:2582 lib/RT/Ticket_Overlay.pm:2640 lib/RT/Ticket_Overlay.pm:2691 lib/RT/Ticket_Overlay.pm:2706 lib/RT/Ticket_Overlay.pm:2905 lib/RT/Ticket_Overlay.pm:2915 lib/RT/Ticket_Overlay.pm:2920 lib/RT/Ticket_Overlay.pm:3143 lib/RT/Ticket_Overlay.pm:3147 lib/RT/Ticket_Overlay.pm:3350 lib/RT/Ticket_Overlay.pm:3512 lib/RT/Ticket_Overlay.pm:3564 lib/RT/Ticket_Overlay.pm:3791 lib/RT/Transaction_Overlay.pm:443 lib/RT/Transaction_Overlay.pm:450 lib/RT/Transaction_Overlay.pm:479 lib/RT/Transaction_Overlay.pm:486 lib/RT/User_Overlay.pm:1088 lib/RT/User_Overlay.pm:1536 lib/RT/User_Overlay.pm:335 lib/RT/User_Overlay.pm:696 lib/RT/User_Overlay.pm:731 lib/RT/User_Overlay.pm:987
-msgid "Permission Denied"
-msgstr "Malkovich Malkovich"
-
-#: html/User/Elements/Tabs:34
-msgid "Personal Groups"
-msgstr "Malkovich"
-
-#: html/User/Groups/index.html:29 html/User/Groups/index.html:39
-msgid "Personal groups"
-msgstr "Malkovich"
-
-#: html/User/Elements/DelegateRights:36
-msgid "Personal groups:"
-msgstr "Malkovich:"
-
-#: html/Admin/Users/Modify.html:153 html/User/Prefs.html:60
-msgid "Phone numbers"
-msgstr "Malkovich"
-
-#: html/Elements/Header:63 html/Elements/Tabs:55 html/SelfService/Elements/Tabs:50 html/SelfService/Prefs.html:24 html/User/Prefs.html:24 html/User/Prefs.html:27
-msgid "Preferences"
-msgstr "Malkovich"
-
-#: lib/RT/Action/Generic.pm:169
-msgid "Prepare Stubbed"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/Tabs:62
-msgid "Prev"
-msgstr "Prev"
-
-#: lib/RT/ACE_Overlay.pm:132 lib/RT/ACE_Overlay.pm:207 lib/RT/ACE_Overlay.pm:551
-#. ($args{'PrincipalId'})
-msgid "Principal %1 not found."
-msgstr "Malkovich %1 Malkovich."
-
-#: html/Search/Elements/PickBasics:58 html/Ticket/Create.html:153 html/Ticket/Elements/EditBasics:52 html/Ticket/Elements/ShowBasics:50 lib/RT/Tickets_Overlay.pm:1104
-msgid "Priority"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/Modify.html:64
-msgid "Priority starts at"
-msgstr "Malkovich at"
-
-#: etc/initialdata:25
-msgid "Privileged"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:296 html/User/Prefs.html:200
-#. (loc_fuzzy($msg))
-msgid "Privileged status: %1"
-msgstr "Malkovich Malkovich: %1"
-
-#: html/Admin/Users/index.html:61
-msgid "Privileged users"
-msgstr "Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Projects"
-msgstr "Malkovich"
-
-#: etc/initialdata:23 etc/initialdata:29 etc/initialdata:35 etc/initialdata:59
-msgid "Pseudogroup for internal use"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Elements/QuickCreate:10 html/Elements/Quicksearch:28 html/Search/Elements/PickBasics:94 html/SelfService/Create.html:32 html/Ticket/Create.html:37 html/Ticket/Elements/EditBasics:35 html/Ticket/Elements/ShowBasics:54 html/User/Elements/DelegateRights:79 lib/RT/Tickets_Overlay.pm:945
-msgid "Queue"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/CustomField.html:41 html/Admin/Queues/Scrip.html:49 html/Admin/Queues/Scrips.html:47 html/Admin/Queues/Templates.html:43
-#. ($Queue)
-#. ($id)
-msgid "Queue %1 not found"
-msgstr "Malkovich %1 Malkovich"
-
-#: html/Admin/Queues/Modify.html:42
-msgid "Queue Name"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:300
-msgid "Queue already exists"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:309 lib/RT/Queue_Overlay.pm:315
-msgid "Queue could not be created"
-msgstr "Malkovich not be Malkovich"
-
-#: html/Ticket/Create.html:208
-msgid "Queue could not be loaded."
-msgstr "Malkovich be Malkovich."
-
-#: docs/design_docs/string-extraction-guide.txt:83 lib/RT/Queue_Overlay.pm:319 lib/RT/StyleGuide.pod:789
-msgid "Queue created"
-msgstr "Malkovich"
-
-#: html/SelfService/Display.html:72 lib/RT/CustomField_Overlay.pm:97
-msgid "Queue not found"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/Tabs:37 html/Admin/index.html:34
-msgid "Queues"
-msgstr "Malkovich"
-
-#: html/Elements/Quicksearch:24
-msgid "Quick search"
-msgstr "Malkovich"
-
-#: html/Elements/Login:44
-#. ($RT::VERSION)
-msgid "RT %1"
-msgstr "RT %1"
-
-#: docs/design_docs/string-extraction-guide.txt:70 lib/RT/StyleGuide.pod:776
-#. ($RT::VERSION, $RT::rtname)
-msgid "RT %1 for %2"
-msgstr "RT %1 for %2"
-
-#: NOT FOUND IN SOURCE
-msgid "RT %1 from <a href=\"http://bestpractical.com\">Best Practical Solutions, LLC</a>."
-msgstr "RT %1 from <a href=\"http://Malkovich.com\">Malkovich Malkovich, LLC</a>."
-
-#: html/Admin/index.html:24 html/Admin/index.html:25
-msgid "RT Administration"
-msgstr "RT Malkovich"
-
-#: html/Elements/Error:41 html/SelfService/Error.html:40
-msgid "RT Error"
-msgstr "RT Malkovich"
-
-#: html/index.html:50 html/index.html:53
-msgid "RT at a glance"
-msgstr "RT at a Malkovich"
-
-#: html/Elements/PageLayout:85
-#. ($RT::rtname)
-msgid "RT for %1"
-msgstr "RT for %1"
-
-#: NOT FOUND IN SOURCE
-msgid "RT is &copy; Copyright 1996-%1 Jesse Vincent <jesse@bestpractical.com>. It is distributed under <a href=\"http://www.gnu.org/copyleft/gpl.html\">Version 2 of the GNU General Public License.</a>"
-msgstr "RT is &copy; Malkovich 1996-%1 Malkovich <Malkovich@Malkovich.com>. It is Malkovich Malkovich <a href=\"http://www.gnu.org/copyleft/gpl.html\">Malkovich 2 of the Malkovich Malkovich Malkovich.</a>"
-
-#: html/Admin/Users/Modify.html:57 html/User/Prefs.html:47
-msgid "Real Name"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "RealName"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:628
-#. ($value)
-msgid "Reference by %1 added"
-msgstr "Malkovich by %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:668
-#. ($value)
-msgid "Reference by %1 deleted"
-msgstr "Malkovich by %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:625
-#. ($value)
-msgid "Reference to %1 added"
-msgstr "Malkovich to %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:665
-#. ($value)
-msgid "Reference to %1 deleted"
-msgstr "Malkovich to %1 Malkovich"
-
-#: html/Elements/EditLinks:129 html/Elements/EditLinks:81 html/Elements/ShowLinks:70 html/Ticket/Create.html:185 html/Ticket/Elements/BulkLinks:50
-msgid "Referred to by"
-msgstr "Malkovich to by"
-
-#: html/Elements/EditLinks:125 html/Elements/EditLinks:72 html/Elements/SelectLinkType:27 html/Elements/ShowLinks:60 html/Ticket/Create.html:184 html/Ticket/Elements/BulkLinks:46
-msgid "Refers to"
-msgstr "Malkovich to"
-
-#: NOT FOUND IN SOURCE
-msgid "Refine search"
-msgstr "Malkovich"
-
-#: html/Elements/Refresh:35
-#. ($value/60)
-msgid "Refresh this page every %1 minutes."
-msgstr "Malkovich Malkovich %1 Malkovich."
-
-#: html/Search/Bulk.html:95
-msgid "Remove AdminCc"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:91
-msgid "Remove Cc"
-msgstr "Malkovich Cc"
-
-#: html/Search/Bulk.html:87
-msgid "Remove Requestor"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/Elements/ShowTransaction:142 html/Ticket/Elements/Tabs:123
-msgid "Reply"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:87
-msgid "Reply to tickets"
-msgstr "Malkovich to Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:87
-msgid "ReplyToTicket"
-msgstr "Malkovich"
-
-#: etc/initialdata:44 lib/RT/ACE_Overlay.pm:86
-msgid "Requestor"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Requestor email address"
-msgstr "Malkovich Malkovich"
-
-#: html/SelfService/Create.html:40 html/Ticket/Create.html:55 html/Ticket/Elements/EditPeople:47 html/Ticket/Elements/ShowPeople:30
-msgid "Requestors"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/Modify.html:74
-msgid "Requests should be due in"
-msgstr "Malkovich be due in"
-
-#: html/Elements/Submit:61
-msgid "Reset"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:156 html/User/Prefs.html:63
-msgid "Residence"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/Tabs:133
-msgid "Resolve"
-msgstr "Malkovich"
-
-#: html/Ticket/Update.html:119
-#. ($TicketObj->id, $TicketObj->Subject)
-msgid "Resolve ticket #%1 (%2)"
-msgstr "Malkovich #%1 (%2)"
-
-#: etc/initialdata:323 html/Elements/SelectDateType:27 lib/RT/Ticket_Overlay.pm:1282
-msgid "Resolved"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Response to requestors"
-msgstr "Malkovich to Malkovich"
-
-#: html/Elements/ListActions:25 html/Search/Elements/NewListActions:25
-msgid "Results"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Results per page"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Users/Modify.html:99 html/User/Prefs.html:94
-msgid "Retype Password"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:612
-msgid "Right Delegated"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:302
-msgid "Right Granted"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:160
-msgid "Right Loaded"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:677 lib/RT/ACE_Overlay.pm:692
-msgid "Right could not be revoked"
-msgstr "Malkovich be Malkovich"
-
-#: html/User/Delegation.html:63
-msgid "Right not found"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:542 lib/RT/ACE_Overlay.pm:637
-msgid "Right not loaded."
-msgstr "Malkovich Malkovich."
-
-#: lib/RT/ACE_Overlay.pm:688
-msgid "Right revoked"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Rights"
-msgstr "Malkovich"
-
-#: lib/RT/Interface/Web.pm:869
-#. ($object_type)
-msgid "Rights could not be granted for %1"
-msgstr "Malkovich Malkovich be Malkovich %1"
-
-#: lib/RT/Interface/Web.pm:899
-#. ($object_type)
-msgid "Rights could not be revoked for %1"
-msgstr "Malkovich Malkovich be Malkovich %1"
-
-#: html/Admin/Global/GroupRights.html:50 html/Admin/Queues/GroupRights.html:52
-msgid "Roles"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:393
-msgid "Sat."
-msgstr "Sat."
-
-#: html/Admin/Global/Template.html:45 html/Admin/Queues/Modify.html:89 html/Admin/Queues/People.html:104 html/Admin/Users/Modify.html:198 html/SelfService/Prefs.html:36 html/Ticket/Modify.html:38 html/Ticket/ModifyAll.html:93 html/Ticket/ModifyDates.html:38 html/Ticket/ModifyLinks.html:38 html/Ticket/ModifyPeople.html:37
-msgid "Save Changes"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/PreviewScrips:79
-msgid "Save changes"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Scrip.html:48 html/Admin/Queues/Scrip.html:54
-#. ($id)
-#. ($ARGS{'id'})
-msgid "Scrip #%1"
-msgstr "Malkovich #%1"
-
-#: lib/RT/Scrip_Overlay.pm:180
-msgid "Scrip Created"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrips:85
-msgid "Scrip deleted"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/QueueTabs:45 html/Admin/Elements/SystemTabs:32 html/Admin/Global/index.html:40
-msgid "Scrips"
-msgstr "Malkovich"
-
-#: html/Admin/Queues/Scrips.html:33
-msgid "Scrips which apply to all queues"
-msgstr "Malkovich Malkovich to Malkovich"
-
-#: html/Elements/SimpleSearch:26 html/Search/Elements/DisplayOptions:73
-msgid "Search"
-msgstr "Malkovich"
-
-#: html/Approvals/Elements/PendingMyApproval:38
-msgid "Search for approvals"
-msgstr "Malkovich Malkovich"
-
-#: bin/rt-crontool:184
-msgid "Security:"
-msgstr "Malkovich:"
-
-#: lib/RT/Queue_Overlay.pm:68
-msgid "SeeQueue"
-msgstr "Malkovich"
-
-#: html/Admin/Groups/index.html:50
-msgid "Select a group"
-msgstr "Malkovich a Malkovich"
-
-#: html/Admin/Users/index.html:24 html/Admin/Users/index.html:27
-msgid "Select a user"
-msgstr "Malkovich a user"
-
-#: html/Admin/Global/CustomField.html:37 html/Admin/Global/CustomFields.html:35
-msgid "Select custom field"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/GroupTabs:51 html/User/Elements/GroupTabs:49
-msgid "Select group"
-msgstr "Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:427
-msgid "Select multiple values"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:424
-msgid "Select one value"
-msgstr "Malkovich Malkovich"
-
-#: html/Admin/Elements/QueueTabs:66
-msgid "Select queue"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Scrip.html:36 html/Admin/Global/Scrips.html:35 html/Admin/Queues/Scrip.html:39 html/Admin/Queues/Scrips.html:51
-msgid "Select scrip"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Template.html:56 html/Admin/Global/Templates.html:35 html/Admin/Queues/Template.html:54 html/Admin/Queues/Templates.html:46
-msgid "Select template"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/UserTabs:46
-msgid "Select user"
-msgstr "Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:35
-msgid "SelectMultiple"
-msgstr "Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:34
-msgid "SelectSingle"
-msgstr "Malkovich"
-
-#: etc/initialdata:121
-msgid "Send mail to all watchers"
-msgstr "Malkovich to Malkovich"
-
-#: etc/initialdata:117
-msgid "Send mail to all watchers as a \"comment\""
-msgstr "Malkovich to Malkovich as a \"Malkovich\""
-
-#: etc/initialdata:112
-msgid "Send mail to requestors and Ccs"
-msgstr "Malkovich to Malkovich and Ccs"
-
-#: etc/initialdata:107
-msgid "Send mail to requestors and Ccs as a comment"
-msgstr "Malkovich to Malkovich and Ccs as a Malkovich"
-
-#: etc/initialdata:78
-msgid "Sends a message to the requestors"
-msgstr "Malkovich a Malkovich to the Malkovich"
-
-#: etc/initialdata:125 etc/initialdata:129
-msgid "Sends mail to explicitly listed Ccs and Bccs"
-msgstr "Malkovich to Malkovich Malkovich and Bccs"
-
-#: etc/initialdata:102
-msgid "Sends mail to the administrative Ccs"
-msgstr "Malkovich to the Malkovich Malkovich"
-
-#: etc/initialdata:98
-msgid "Sends mail to the administrative Ccs as a comment"
-msgstr "Malkovich to the Malkovich Malkovich as a Malkovich"
-
-#: etc/initialdata:82 etc/initialdata:86
-msgid "Sends mail to the owner"
-msgstr "Malkovich to the Malkovich"
-
-#: lib/RT/Date.pm:419
-msgid "Sep."
-msgstr "Sep."
-
-#: html/Approvals/Elements/PendingMyApproval:43
-msgid "Show approved requests"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/Create.html:143 html/Ticket/Create.html:33
-msgid "Show basics"
-msgstr "Malkovich"
-
-#: html/Approvals/Elements/PendingMyApproval:44
-msgid "Show denied requests"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/Create.html:143 html/Ticket/Create.html:33
-msgid "Show details"
-msgstr "Malkovich"
-
-#: html/Approvals/Elements/PendingMyApproval:42
-msgid "Show pending requests"
-msgstr "Malkovich Malkovich"
-
-#: html/Approvals/Elements/PendingMyApproval:45
-msgid "Show requests awaiting other approvals"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Show ticket private commentary"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Show ticket summaries"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:70
-msgid "ShowACL"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:79
-msgid "ShowScrips"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:76
-msgid "ShowTemplate"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:80
-msgid "ShowTicket"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:81
-msgid "ShowTicketComments"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:84
-msgid "Sign up as a ticket Requestor or ticket or queue Cc"
-msgstr "Sign up as a Malkovich Malkovich or Malkovich or Malkovich Cc"
-
-#: lib/RT/Queue_Overlay.pm:85
-msgid "Sign up as a ticket or queue AdminCc"
-msgstr "Sign up as a Malkovich or Malkovich"
-
-#: html/Admin/Users/Modify.html:188 html/User/Prefs.html:145
-msgid "Signature"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectSingleOrMultiple:25
-msgid "Single"
-msgstr "Malkovich"
-
-#: html/Elements/Header:62
-msgid "Skip Menu"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/AddCustomFieldValue:27
-msgid "Sort"
-msgstr "Sort"
-
-#: NOT FOUND IN SOURCE
-msgid "Sort results by"
-msgstr "Malkovich by"
-
-#: NOT FOUND IN SOURCE
-msgid "Squelched message recipients"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/Admin/Elements/EditScrip:65
-msgid "Stage"
-msgstr "Malkovich"
-
-#: html/Elements/SelectDateType:26 html/Ticket/Elements/EditDates:31 html/Ticket/Elements/ShowDates:35
-msgid "Started"
-msgstr "Malkovich"
-
-#: html/Elements/SelectDateType:30 html/Ticket/Create.html:165 html/Ticket/Elements/EditDates:26 html/Ticket/Elements/ShowDates:31
-msgid "Starts"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:135 html/User/Prefs.html:123
-msgid "State"
-msgstr "Malkovich"
-
-#: html/Search/Elements/PickBasics:77 html/SelfService/Elements/MyRequests:28 html/SelfService/Update.html:30 html/Ticket/Create.html:41 html/Ticket/Elements/EditBasics:31 html/Ticket/Elements/ShowBasics:30 html/Ticket/Update.html:37 lib/RT/Ticket_Overlay.pm:1276 lib/RT/Tickets_Overlay.pm:970
-msgid "Status"
-msgstr "Malkovich"
-
-#: etc/initialdata:309
-msgid "Status Change"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:505
-#. ($self->loc($self->OldValue), $self->loc($self->NewValue))
-msgid "Status changed from %1 to %2"
-msgstr "Malkovich Malkovich %1 to %2"
-
-#: html/Ticket/Elements/Tabs:148
-msgid "Steal"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:94
-msgid "Steal tickets"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:94
-msgid "StealTicket"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:581
-#. ($Old->Name)
-msgid "Stolen from %1 "
-msgstr "Malkovich %1 "
-
-#: html/Elements/QuickCreate:7 html/Elements/SelectAttachmentField:25 html/Search/Bulk.html:133 html/SelfService/Create.html:56 html/SelfService/Elements/MyRequests:27 html/SelfService/Update.html:31 html/Ticket/Create.html:83 html/Ticket/Elements/EditBasics:26 html/Ticket/ModifyAll.html:78 html/Ticket/Update.html:58 lib/RT/Ticket_Overlay.pm:1272 lib/RT/Tickets_Overlay.pm:1049
-msgid "Subject"
-msgstr "Malkovich"
-
-#: docs/design_docs/string-extraction-guide.txt:89 lib/RT/StyleGuide.pod:795 lib/RT/Transaction_Overlay.pm:603
-#. ($self->Data)
-msgid "Subject changed to %1"
-msgstr "Malkovich to %1"
-
-#: html/Elements/Submit:58
-msgid "Submit"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:749
-msgid "Succeeded"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:394
-msgid "Sun."
-msgstr "Sun."
-
-#: lib/RT/System.pm:53
-msgid "SuperUser"
-msgstr "Malkovich"
-
-#: html/User/Elements/DelegateRights:76
-msgid "System"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectRights:81 lib/RT/ACE_Overlay.pm:566 lib/RT/Interface/Web.pm:868 lib/RT/Interface/Web.pm:898
-msgid "System Error"
-msgstr "Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:615
-msgid "System error. Right not delegated."
-msgstr "Malkovich. Malkovich Malkovich."
-
-#: lib/RT/ACE_Overlay.pm:145 lib/RT/ACE_Overlay.pm:222 lib/RT/ACE_Overlay.pm:305 lib/RT/ACE_Overlay.pm:897
-msgid "System error. Right not granted."
-msgstr "Malkovich. Malkovich Malkovich."
-
-#: html/Admin/Global/GroupRights.html:34 html/Admin/Groups/GroupRights.html:36 html/Admin/Queues/GroupRights.html:35
-msgid "System groups"
-msgstr "Malkovich"
-
-#: etc/initialdata:41 etc/initialdata:47 etc/initialdata:53
-msgid "SystemRolegroup for internal use"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/CurrentUser.pm:334
-msgid "TEST_STRING"
-msgstr "TEST_MALKOVICH"
-
-#: html/Elements/MyRequests:27 html/Ticket/Elements/Tabs:144
-msgid "Take"
-msgstr "Take"
-
-#: lib/RT/Queue_Overlay.pm:92
-msgid "Take tickets"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:92
-msgid "TakeTicket"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:566
-msgid "Taken"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditScrip:57 html/Tools/Offline.html:56
-msgid "Template"
-msgstr "Malkovich"
-
-#: html/Admin/Global/Template.html:90 html/Admin/Queues/Template.html:89
-#. ($TemplateObj->Id())
-msgid "Template #%1"
-msgstr "Malkovich #%1"
-
-#: html/Admin/Elements/EditTemplates:88
-msgid "Template deleted"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Scrip_Overlay.pm:156
-msgid "Template not found"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Template_Overlay.pm:348
-msgid "Template parsed"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/QueueTabs:48 html/Admin/Elements/SystemTabs:35 html/Admin/Global/index.html:44
-msgid "Templates"
-msgstr "Malkovich"
-
-#: lib/RT/Record.pm:740
-msgid "That is already the current value"
-msgstr "That is Malkovich the Malkovich"
-
-#: lib/RT/CustomField_Overlay.pm:248
-msgid "That is not a value for this custom field"
-msgstr "That is not a Malkovich Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2086
-msgid "That is the same value"
-msgstr "That is the Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:287 lib/RT/ACE_Overlay.pm:596
-msgid "That principal already has that right"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:674
-#. ($args{'Type'})
-msgid "That principal is already a %1 for this queue"
-msgstr "Malkovich is Malkovich a %1 Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1527
-#. ($self->loc($args{'Type'}))
-msgid "That principal is already a %1 for this ticket"
-msgstr "Malkovich is Malkovich a %1 Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:773
-#. ($args{'Type'})
-msgid "That principal is not a %1 for this queue"
-msgstr "That Malkovich is not a %1 Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2082
-msgid "That queue does not exist"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3152
-msgid "That ticket has unresolved dependencies"
-msgstr "Malkovich Malkovich Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2956
-msgid "That user already owns that ticket"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2928
-msgid "That user does not exist"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:355
-msgid "That user is already privileged"
-msgstr "Malkovich is Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:376
-msgid "That user is already unprivileged"
-msgstr "Malkovich is Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:368
-msgid "That user is now privileged"
-msgstr "Malkovich is Malkovich"
-
-#: lib/RT/User_Overlay.pm:389
-msgid "That user is now unprivileged"
-msgstr "Malkovich is Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:2949
-msgid "That user may not own tickets in that queue"
-msgstr "Malkovich Malkovich Malkovich in Malkovich"
-
-#: lib/RT/Link_Overlay.pm:200
-msgid "That's not a numerical id"
-msgstr "That's not a Malkovich id"
-
-#: html/SelfService/Display.html:31 html/Ticket/Create.html:149 html/Ticket/Elements/ShowSummary:27
-msgid "The Basics"
-msgstr "The Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:87
-msgid "The CC of a ticket"
-msgstr "The CC of a Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:88
-msgid "The administrative CC of a ticket"
-msgstr "The Malkovich CC of a Malkovich"
-
-#: bin/rt-crontool:194
-msgid "The following command will find all active tickets in the queue 'general' and set their priority to 99 if they haven't been touched in 4 hours:"
-msgstr "The Malkovich Malkovich Malkovich Malkovich Malkovich in the Malkovich 'Malkovich' and Malkovich Malkovich to 99 if they haven't Malkovich in 4 Malkovich:"
-
-#: NOT FOUND IN SOURCE
-msgid "The following commands were not proccessed:\\n\\n"
-msgstr "The Malkovich Malkovich Malkovich Malkovich:\\n\\n"
-
-#: lib/RT/Record.pm:743
-msgid "The new value has been set."
-msgstr "The Malkovich Malkovich."
-
-#: lib/RT/ACE_Overlay.pm:85
-msgid "The owner of a ticket"
-msgstr "The Malkovich of a Malkovich"
-
-#: lib/RT/ACE_Overlay.pm:86
-msgid "The requestor of a ticket"
-msgstr "The Malkovich of a Malkovich"
-
-#: html/Admin/Elements/EditUserComments:25
-msgid "These comments aren't generally visible to the user"
-msgstr "Malkovich aren't Malkovich Malkovich to the user"
-
-#: bin/rt-crontool:185
-msgid "This tool allows the user to run arbitrary perl modules from within RT."
-msgstr "Malkovich Malkovich the user to Malkovich Malkovich Malkovich Malkovich RT."
-
-#: lib/RT/Transaction_Overlay.pm:226
-msgid "This transaction appears to have no content"
-msgstr "Malkovich Malkovich to have no Malkovich"
-
-#: html/Ticket/Elements/ShowRequestor:48
-#. ($rows)
-msgid "This user's %1 highest priority tickets"
-msgstr "Malkovich's %1 Malkovich Malkovich"
-
-#: lib/RT/Date.pm:391
-msgid "Thu."
-msgstr "Thu."
-
-#: html/Ticket/ModifyAll.html:24 html/Ticket/ModifyAll.html:28
-#. ($Ticket->Id, $Ticket->Subject)
-msgid "Ticket #%1 Jumbo update: %2"
-msgstr "Malkovich #%1 Malkovich: %2"
-
-#: html/Approvals/Elements/ShowDependency:45
-#. ($link->BaseObj->Id, $link->BaseObj->Subject)
-msgid "Ticket #%1: %2"
-msgstr "Malkovich #%1: %2"
-
-#: lib/RT/Ticket_Overlay.pm:696 lib/RT/Ticket_Overlay.pm:720
-#. ($self->Id, $QueueObj->Name)
-msgid "Ticket %1 created in queue '%2'"
-msgstr "Malkovich %1 Malkovich in Malkovich '%2'"
-
-#: NOT FOUND IN SOURCE
-msgid "Ticket %1 loaded\\n"
-msgstr "Malkovich %1 Malkovich\\n"
-
-#: html/Search/Bulk.html:216
-#. ($Ticket->Id,$_)
-msgid "Ticket %1: %2"
-msgstr "Malkovich %1: %2"
-
-#: html/Ticket/History.html:24 html/Ticket/History.html:27
-#. ($Ticket->Id, $Ticket->Subject)
-msgid "Ticket History # %1 %2"
-msgstr "Malkovich # %1 %2"
-
-#: etc/initialdata:324
-msgid "Ticket Resolved"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Ticket attachment"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1228
-msgid "Ticket content"
-msgstr "Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1274
-msgid "Ticket content type"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:565 lib/RT/Ticket_Overlay.pm:579 lib/RT/Ticket_Overlay.pm:590 lib/RT/Ticket_Overlay.pm:707
-msgid "Ticket could not be created due to an internal error"
-msgstr "Malkovich Malkovich be Malkovich to a Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:497
-msgid "Ticket created"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:502
-msgid "Ticket deleted"
-msgstr "Malkovich"
-
-#: etc/initialdata:310
-msgid "Ticket status changed"
-msgstr "Malkovich Malkovich"
-
-#: html/Elements/Tabs:46
-msgid "Tickets"
-msgstr "Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1452
-#. ($self->loc($args{'TYPE'}), ($args{'BASE'} || $args{'TICKET'}))
-msgid "Tickets %1 %2"
-msgstr "Malkovich %1 %2"
-
-#: lib/RT/Tickets_Overlay.pm:1410
-#. ($self->loc($args{'TYPE'}), ($args{'TARGET'} || $args{'TICKET'}))
-msgid "Tickets %1 by %2"
-msgstr "Malkovich %1 by %2"
-
-#: NOT FOUND IN SOURCE
-msgid "Tickets from %1"
-msgstr "Malkovich %1"
-
-#: html/Approvals/Elements/ShowDependency:26
-msgid "Tickets which depend on this approval:"
-msgstr "Malkovich Malkovich on Malkovich:"
-
-#: html/Search/Elements/PickBasics:70 html/Ticket/Create.html:156 html/Ticket/Elements/EditBasics:47
-msgid "Time Left"
-msgstr "Malkovich"
-
-#: html/Search/Elements/PickBasics:68 html/Ticket/Create.html:155 html/Ticket/Elements/EditBasics:43
-msgid "Time Worked"
-msgstr "Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1201
-msgid "Time left"
-msgstr "Malkovich"
-
-#: html/Elements/Footer:44
-msgid "Time to display"
-msgstr "Time to Malkovich"
-
-#: lib/RT/Tickets_Overlay.pm:1177
-msgid "Time worked"
-msgstr "Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:1277
-msgid "TimeWorked"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "To generate a diff of this commit:"
-msgstr "To Malkovich a diff of Malkovich:"
-
-#: NOT FOUND IN SOURCE
-msgid "To generate a diff of this commit:\\n"
-msgstr "To Malkovich a diff of Malkovich:\\n"
-
-#: lib/RT/Ticket_Overlay.pm:1280
-msgid "Told"
-msgstr "Told"
-
-#: etc/initialdata:252
-msgid "Transaction"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:705
-#. ($self->Data)
-msgid "Transaction %1 purged"
-msgstr "Malkovich %1 Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:136
-msgid "Transaction Created"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:92
-msgid "Transaction->Create couldn't, as you didn't specify a ticket id"
-msgstr "Malkovich->Malkovich couldn't, as you didn't Malkovich a Malkovich id"
-
-#: lib/RT/Transaction_Overlay.pm:760
-msgid "Transactions are immutable"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Date.pm:389
-msgid "Tue."
-msgstr "Tue."
-
-#: html/Admin/Elements/EditCustomField:43 html/Ticket/Elements/AddWatchers:32 html/Ticket/Elements/AddWatchers:43 html/Ticket/Elements/AddWatchers:53 lib/RT/Ticket_Overlay.pm:1278 lib/RT/Tickets_Overlay.pm:1021
-msgid "Type"
-msgstr "Type"
-
-#: lib/RT/ScripCondition_Overlay.pm:103
-msgid "Unimplemented"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:67
-msgid "Unix login"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "UnixUsername"
-msgstr "Malkovich"
-
-#: lib/RT/Attachment_Overlay.pm:233 lib/RT/Attachment_Overlay.pm:265
-#. ($self->ContentEncoding)
-msgid "Unknown ContentEncoding %1"
-msgstr "Malkovich Malkovich %1"
-
-#: html/Elements/SelectResultsPerPage:36
-msgid "Unlimited"
-msgstr "Malkovich"
-
-#: etc/initialdata:32
-msgid "Unprivileged"
-msgstr "Malkovich"
-
-#: lib/RT/Transaction_Overlay.pm:562
-msgid "Untaken"
-msgstr "Malkovich"
-
-#: html/Search/Bulk.html:32
-msgid "Update"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Update ID"
-msgstr "Malkovich ID"
-
-#: html/Search/Bulk.html:127 html/Ticket/ModifyAll.html:65 html/Ticket/Update.html:48
-msgid "Update Type"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Update all these tickets at once"
-msgstr "Malkovich Malkovich at once"
-
-#: NOT FOUND IN SOURCE
-msgid "Update email"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Update name"
-msgstr "Malkovich"
-
-#: lib/RT/Action/CreateTickets.pm:655 lib/RT/Interface/Web.pm:479
-msgid "Update not recorded."
-msgstr "Malkovich Malkovich."
-
-#: html/Search/Bulk.html:78
-msgid "Update selected tickets"
-msgstr "Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "Update signature"
-msgstr "Malkovich Malkovich"
-
-#: html/Ticket/ModifyAll.html:62
-msgid "Update ticket"
-msgstr "Malkovich"
-
-#: html/SelfService/Update.html:24 html/SelfService/Update.html:63
-#. ($Ticket->id)
-msgid "Update ticket #%1"
-msgstr "Malkovich #%1"
-
-#: html/Ticket/Update.html:121
-#. ($TicketObj->id, $TicketObj->Subject)
-msgid "Update ticket #%1 (%2)"
-msgstr "Malkovich #%1 (%2)"
-
-#: lib/RT/Action/CreateTickets.pm:653 lib/RT/Interface/Web.pm:477
-msgid "Update type was neither correspondence nor comment."
-msgstr "Malkovich Malkovich Malkovich Malkovich Malkovich."
-
-#: html/Elements/SelectDateType:32 html/Ticket/Elements/ShowDates:51 lib/RT/Ticket_Overlay.pm:1281
-msgid "Updated"
-msgstr "Malkovich"
-
-#: etc/initialdata:132 etc/initialdata:206
-msgid "User Defined"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "User ID"
-msgstr "User ID"
-
-#: html/Elements/SelectUsers:25
-msgid "User Id"
-msgstr "User Id"
-
-#: html/Admin/Elements/GroupTabs:46 html/Admin/Elements/QueueTabs:59 html/Admin/Elements/SystemTabs:46 html/Admin/Global/index.html:58
-msgid "User Rights"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:252
-#. ($msg)
-msgid "User could not be created: %1"
-msgstr "Malkovich be Malkovich: %1"
-
-#: lib/RT/User_Overlay.pm:296
-msgid "User created"
-msgstr "Malkovich"
-
-#: html/Admin/Global/GroupRights.html:66 html/Admin/Groups/GroupRights.html:53 html/Admin/Queues/GroupRights.html:68
-msgid "User defined groups"
-msgstr "Malkovich Malkovich"
-
-#: lib/RT/User_Overlay.pm:558 lib/RT/User_Overlay.pm:575
-msgid "User loaded"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "User view"
-msgstr "Malkovich"
-
-#: html/Admin/Users/Modify.html:47 html/Elements/Login:51 html/Ticket/Elements/AddWatchers:34
-msgid "Username"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/SelectNewGroupMembers:25 html/Admin/Elements/Tabs:31 html/Admin/Groups/Members.html:54 html/Admin/Queues/People.html:67 html/Admin/index.html:28 html/User/Groups/Members.html:57
-msgid "Users"
-msgstr "Malkovich"
-
-#: html/Admin/Users/index.html:64
-msgid "Users matching search criteria"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: lib/RT/Tickets_Overlay_SQL.pm:494
-msgid "Valid Query"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/EditCustomField:56
-msgid "Values"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:84
-msgid "Watch"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:85
-msgid "WatchAsAdminCc"
-msgstr "Malkovich"
-
-#: html/Admin/Elements/QueueTabs:41
-msgid "Watchers"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "WebEncoding"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:390
-msgid "Wed."
-msgstr "Wed."
-
-#: etc/initialdata:521
-msgid "When a ticket has been approved by all approvers, add correspondence to the original ticket"
-msgstr "When a Malkovich Malkovich by Malkovich, Malkovich Malkovich to the Malkovich"
-
-#: etc/initialdata:485
-msgid "When a ticket has been approved by any approver, add correspondence to the original ticket"
-msgstr "When a Malkovich Malkovich by Malkovich, Malkovich Malkovich to the Malkovich"
-
-#: etc/initialdata:146
-msgid "When a ticket is created"
-msgstr "When a Malkovich is Malkovich"
-
-#: etc/initialdata:418
-msgid "When an approval ticket is created, notify the Owner and AdminCc of the item awaiting their approval"
-msgstr "When a Malkovich is Malkovich, Malkovich the Malkovich and Malkovich of the Malkovich Malkovich Malkovich"
-
-#: etc/initialdata:151
-msgid "When anything happens"
-msgstr "Malkovich Malkovich"
-
-#: etc/initialdata:199
-msgid "Whenever a ticket is resolved"
-msgstr "Malkovich a Malkovich is Malkovich"
-
-#: etc/initialdata:185
-msgid "Whenever a ticket's owner changes"
-msgstr "Malkovich a Malkovich's Malkovich"
-
-#: etc/initialdata:193
-msgid "Whenever a ticket's queue changes"
-msgstr "Malkovich a Malkovich's Malkovich"
-
-#: etc/initialdata:170
-msgid "Whenever a ticket's status changes"
-msgstr "Malkovich a Malkovich's Malkovich"
-
-#: etc/initialdata:207
-msgid "Whenever a user-defined condition occurs"
-msgstr "Malkovich a user-Malkovich Malkovich"
-
-#: etc/initialdata:164
-msgid "Whenever comments come in"
-msgstr "Malkovich Malkovich in"
-
-#: etc/initialdata:157
-msgid "Whenever correspondence comes in"
-msgstr "Malkovich Malkovich Malkovich in"
-
-#: html/Admin/Users/Modify.html:161 html/User/Prefs.html:67
-msgid "Work"
-msgstr "Work"
-
-#: NOT FOUND IN SOURCE
-msgid "WorkPhone"
-msgstr "Malkovich"
-
-#: html/Ticket/Elements/ShowBasics:41 html/Ticket/Update.html:42
-msgid "Worked"
-msgstr "Malkovich"
-
-#: html/autohandler:150
-msgid "XXX CHANGEME You are not an authorized user"
-msgstr "MALKOVICH Malkovich a Malkovich"
-
-#: lib/RT/Ticket_Overlay.pm:3059
-msgid "You already own this ticket"
-msgstr "Malkovich Malkovich Malkovich"
-
-#: html/autohandler:142
-msgid "You are not an authorized user"
-msgstr "Malkovich a Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "You can access it with the Download button on the right."
-msgstr "Malkovich it with the Malkovich on the Malkovich."
-
-#: lib/RT/Ticket_Overlay.pm:2941
-msgid "You can only reassign tickets that you own or that are unowned"
-msgstr "Malkovich Malkovich Malkovich Malkovich or Malkovich Malkovich"
-
-#: docs/design_docs/string-extraction-guide.txt:47 lib/RT/StyleGuide.pod:760
-#. ($num, $queue)
-msgid "You found %1 tickets in queue %2"
-msgstr "Malkovich %1 Malkovich in Malkovich %2"
-
-#: html/NoAuth/Logout.html:30
-msgid "You have been logged out of RT."
-msgstr "Malkovich Malkovich of RT."
-
-#: html/SelfService/Display.html:79
-msgid "You have no permission to create tickets in that queue."
-msgstr "Malkovich no Malkovich to Malkovich in that Malkovich."
-
-#: lib/RT/Ticket_Overlay.pm:2095
-msgid "You may not create requests in that queue."
-msgstr "Malkovich Malkovich Malkovich in Malkovich."
-
-#: html/NoAuth/Logout.html:34
-msgid "You're welcome to login again"
-msgstr "You're Malkovich to Malkovich"
-
-#: etc/initialdata:502
-msgid "Your request has been approved by %1. Other approvals may still be pending."
-msgstr "Malkovich Malkovich Malkovich by %1. Malkovich Malkovich be Malkovich."
-
-#: etc/initialdata:540
-msgid "Your request has been approved."
-msgstr "Malkovich Malkovich Malkovich."
-
-#: etc/initialdata:445
-msgid "Your request was rejected."
-msgstr "Malkovich Malkovich."
-
-#: html/autohandler:177
-msgid "Your username or password is incorrect"
-msgstr "Malkovich or Malkovich is Malkovich"
-
-#: html/Admin/Users/Modify.html:141 html/User/Prefs.html:127
-msgid "Zip"
-msgstr "Zip"
-
-#: html/User/Elements/DelegateRights:58
-#. ($right->PrincipalObj->Object->SelfDescription)
-msgid "as granted to %1"
-msgstr "as Malkovich to %1"
-
-#: html/SelfService/Closed.html:27
-msgid "closed"
-msgstr "Malkovich"
-
-#: html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectMatch:33
-msgid "contains"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "content"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "content-type"
-msgstr "Malkovich-type"
-
-#: html/Admin/Queues/Modify.html:76 lib/RT/Date.pm:319
-msgid "days"
-msgstr "days"
-
-#: lib/RT/Queue_Overlay.pm:64
-msgid "deleted"
-msgstr "Malkovich"
-
-#: html/Search/Elements/PickBasics:33
-msgid "does not match"
-msgstr "Malkovich"
-
-#: html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectMatch:34
-msgid "doesn't contain"
-msgstr "doesn't Malkovich"
-
-#: html/Elements/SelectEqualityOperator:37
-msgid "equal to"
-msgstr "Malkovich to"
-
-#: NOT FOUND IN SOURCE
-msgid "filename"
-msgstr "Malkovich"
-
-#: html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectEqualityOperator:37
-msgid "greater than"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:196
-#. ($self->Name)
-msgid "group '%1'"
-msgstr "Malkovich '%1'"
-
-#: lib/RT/Date.pm:315
-msgid "hours"
-msgstr "Malkovich"
-
-#: html/Elements/SelectBoolean:31 html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectMatch:35 html/Search/Elements/PickBasics:49 html/Search/Elements/PickBasics:80 html/Search/Elements/PickBasics:97 html/Search/Elements/PickCFs:37
-msgid "is"
-msgstr "is"
-
-#: html/Elements/SelectBoolean:35 html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectMatch:36 html/Search/Elements/PickBasics:50 html/Search/Elements/PickBasics:81 html/Search/Elements/PickBasics:98 html/Search/Elements/PickCFs:38
-msgid "isn't"
-msgstr "isn't"
-
-#: html/Elements/SelectCustomFieldOperator:37 html/Elements/SelectEqualityOperator:37
-msgid "less than"
-msgstr "Malkovich"
-
-#: html/Search/Elements/PickBasics:32
-msgid "matches"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:311
-msgid "min"
-msgstr "min"
-
-#: html/Ticket/Update.html:42
-msgid "minutes"
-msgstr "Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "modifications\\n\\n"
-msgstr "Malkovich\\n\\n"
-
-#: lib/RT/Date.pm:327
-msgid "months"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:59
-msgid "new"
-msgstr "new"
-
-#: html/Admin/Elements/EditCustomFields:42
-msgid "no name"
-msgstr "no name"
-
-#: html/Admin/Elements/EditScrips:42
-msgid "no value"
-msgstr "no Malkovich"
-
-#: html/Admin/Elements/EditQueueWatchers:26 html/Ticket/Elements/EditWatchers:27
-msgid "none"
-msgstr "none"
-
-#: html/Elements/SelectEqualityOperator:37
-msgid "not equal to"
-msgstr "Malkovich to"
-
-#: html/SelfService/Elements/MyRequests:61 lib/RT/Queue_Overlay.pm:60
-msgid "open"
-msgstr "open"
-
-#: lib/RT/Group_Overlay.pm:201
-#. ($self->Name, $user->Name)
-msgid "personal group '%1' for user '%2'"
-msgstr "Malkovich '%1' Malkovich '%2'"
-
-#: lib/RT/Group_Overlay.pm:209
-#. ($queue->Name, $self->Type)
-msgid "queue %1 %2"
-msgstr "Malkovich %1 %2"
-
-#: lib/RT/Queue_Overlay.pm:63
-msgid "rejected"
-msgstr "Malkovich"
-
-#: lib/RT/Queue_Overlay.pm:62
-msgid "resolved"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:307
-msgid "sec"
-msgstr "sec"
-
-#: lib/RT/Queue_Overlay.pm:61
-msgid "stalled"
-msgstr "Malkovich"
-
-#: lib/RT/Group_Overlay.pm:204
-#. ($self->Type)
-msgid "system %1"
-msgstr "Malkovich %1"
-
-#: lib/RT/Group_Overlay.pm:215
-#. ($self->Type)
-msgid "system group '%1'"
-msgstr "Malkovich '%1'"
-
-#: html/Elements/Error:42 html/SelfService/Error.html:41
-msgid "the calling component did not specify why"
-msgstr "the Malkovich Malkovich Malkovich Malkovich"
-
-#: NOT FOUND IN SOURCE
-msgid "ticket #%1"
-msgstr "Malkovich #%1"
-
-#: lib/RT/Group_Overlay.pm:212
-#. ($self->Instance, $self->Type)
-msgid "ticket #%1 %2"
-msgstr "Malkovich #%1 %2"
-
-#: lib/RT/Group_Overlay.pm:218
-#. ($self->Id)
-msgid "undescribed group %1"
-msgstr "Malkovich Malkovich %1"
-
-#: lib/RT/Group_Overlay.pm:193
-#. ($user->Object->Name)
-msgid "user %1"
-msgstr "user %1"
-
-#: lib/RT/Date.pm:323
-msgid "weeks"
-msgstr "Malkovich"
-
-#: lib/RT/Date.pm:331
-msgid "years"
-msgstr "Malkovich"
-
diff --git a/rt/lib/RT/Interface/CLI.pm b/rt/lib/RT/Interface/CLI.pm
index a910fb48a..ec0e877b4 100644
--- a/rt/lib/RT/Interface/CLI.pm
+++ b/rt/lib/RT/Interface/CLI.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
+# 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.
#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
use strict;
use RT;
@@ -54,12 +29,14 @@ package RT::Interface::CLI;
BEGIN {
- use base 'Exporter';
- use vars qw ($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS);
+ use Exporter ();
+ use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
# set the version for version checking
- $VERSION = do { my @r = (q$Revision: 1.1.1.8 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker
-
+ $VERSION = do { my @r = (q$Revision: 1.2 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker
+
+ @ISA = qw(Exporter);
+
# your exported package globals go here,
# as well as any optionally exported functions
@EXPORT_OK = qw(&CleanEnv
@@ -99,6 +76,11 @@ BEGIN {
=head1 METHODS
+=begin testing
+
+ok(require RT::Interface::CLI);
+
+=end testing
=cut
@@ -220,7 +202,7 @@ sub GetMessageContent {
if ($edit) {
unless ($ENV{'EDITOR'}) {
- $RT::Logger->crit('No $EDITOR variable defined');
+ $RT::Logger->crit('No $EDITOR variable defined'. "\n");
return undef;
}
system ($ENV{'EDITOR'}, $filename);
@@ -243,7 +225,7 @@ sub debug {
my $val = shift;
my ($debug);
if ($val) {
- $RT::Logger->debug($val);
+ $RT::Logger->debug($val."\n");
if ($debug) {
print STDERR "$val\n";
}
diff --git a/rt/lib/RT/Interface/Email.pm b/rt/lib/RT/Interface/Email.pm
index b669b5b2f..7eec0502f 100755
--- a/rt/lib/RT/Interface/Email.pm
+++ b/rt/lib/RT/Interface/Email.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,72 +14,50 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
package RT::Interface::Email;
use strict;
-use warnings;
-
-use Email::Address;
+use Mail::Address;
use MIME::Entity;
use RT::EmailParser;
-use File::Temp;
-use UNIVERSAL::require;
-use Mail::Mailer ();
-BEGIN {
- use base 'Exporter';
- use vars qw ( @EXPORT_OK);
+BEGIN {
+ use Exporter ();
+ use vars qw ($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+
# set the version for version checking
- our $VERSION = 2.0;
-
+ $VERSION = do { my @r = (q$Revision: 1.2 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker
+
+ @ISA = qw(Exporter);
+
# your exported package globals go here,
# as well as any optionally exported functions
- @EXPORT_OK = qw(
- &CreateUser
- &GetMessageContent
- &CheckForLoops
- &CheckForSuspiciousSender
- &CheckForAutoGenerated
- &CheckForBounce
- &MailError
- &ParseCcAddressesFromHead
- &ParseSenderAddressFromHead
- &ParseErrorsToAddressFromHead
- &ParseAddressFromHeader
- &Gateway);
+ @EXPORT_OK = qw(
+ &CreateUser
+ &GetMessageContent
+ &CheckForLoops
+ &CheckForSuspiciousSender
+ &CheckForAutoGenerated
+ &MailError
+ &ParseCcAddressesFromHead
+ &ParseSenderAddressFromHead
+ &ParseErrorsToAddressFromHead
+ &ParseAddressFromHeader
+ &Gateway);
}
=head1 NAME
- RT::Interface::Email - helper functions for parsing email sent to RT
+ RT::Interface::CLI - helper functions for creating a commandline RT interface
=head1 SYNOPSIS
@@ -97,1605 +69,425 @@ BEGIN {
=head1 DESCRIPTION
+=begin testing
+ok(require RT::Interface::Email);
-=head1 METHODS
+=end testing
-=head2 CheckForLoops HEAD
-Takes a HEAD object of L<MIME::Head> class and returns true if the
-message's been sent by this RT instance. Uses "X-RT-Loop-Prevention"
-field of the head for test.
+=head1 METHODS
=cut
-sub CheckForLoops {
- my $head = shift;
- # If this instance of RT sent it our, we don't want to take it in
+# {{{ sub CheckForLoops
+
+sub CheckForLoops {
+ my $head = shift;
+
+ #If this instance of RT sent it our, we don't want to take it in
my $RTLoop = $head->get("X-RT-Loop-Prevention") || "";
- chomp ($RTLoop); # remove that newline
- if ( $RTLoop eq RT->Config->Get('rtname') ) {
- return 1;
+ chomp ($RTLoop); #remove that newline
+ if ($RTLoop eq "$RT::rtname") {
+ return (1);
}
-
+
# TODO: We might not trap the case where RT instance A sends a mail
# to RT instance B which sends a mail to ...
- return undef;
+ return (undef);
}
-=head2 CheckForSuspiciousSender HEAD
+# }}}
-Takes a HEAD object of L<MIME::Head> class and returns true if sender
-is suspicious. Suspicious means mailer daemon.
-
-See also L</ParseSenderAddressFromHead>.
-
-=cut
+# {{{ sub CheckForSuspiciousSender
sub CheckForSuspiciousSender {
my $head = shift;
#if it's from a postmaster or mailer daemon, it's likely a bounce.
-
+
#TODO: better algorithms needed here - there is no standards for
#bounces, so it's very difficult to separate them from anything
#else. At the other hand, the Return-To address is only ment to be
#used as an error channel, we might want to put up a separate
#Return-To address which is treated differently.
-
+
#TODO: search through the whole email and find the right Ticket ID.
- my ( $From, $junk ) = ParseSenderAddressFromHead($head);
-
- if ( ( $From =~ /^mailer-daemon\@/i )
- or ( $From =~ /^postmaster\@/i )
- or ( $From eq "" ))
- {
- return (1);
-
+ my ($From, $junk) = ParseSenderAddressFromHead($head);
+
+ if (($From =~ /^mailer-daemon/i) or
+ ($From =~ /^postmaster/i)){
+ return (1);
+
}
+
+ return (undef);
- return undef;
}
-=head2 CheckForAutoGenerated HEAD
-
-Takes a HEAD object of L<MIME::Head> class and returns true if message
-is autogenerated. Checks 'Precedence' and 'X-FC-Machinegenerated'
-fields of the head in tests.
-
-=cut
+# }}}
+# {{{ sub CheckForAutoGenerated
sub CheckForAutoGenerated {
my $head = shift;
-
- my $Precedence = $head->get("Precedence") || "";
- if ( $Precedence =~ /^(bulk|junk)/i ) {
- return (1);
- }
-
- # Per RFC3834, any Auto-Submitted header which is not "no" means
- # it is auto-generated.
- my $AutoSubmitted = $head->get("Auto-Submitted") || "";
- if ( length $AutoSubmitted and $AutoSubmitted ne "no" ) {
- return (1);
+
+ my $Precedence = $head->get("Precedence") || "" ;
+ if ($Precedence =~ /^(bulk|junk)/i) {
+ return (1);
}
-
- # First Class mailer uses this as a clue.
- my $FCJunk = $head->get("X-FC-Machinegenerated") || "";
- if ( $FCJunk =~ /^true/i ) {
- return (1);
+ else {
+ return (0);
}
-
- return (0);
}
+# }}}
-sub CheckForBounce {
- my $head = shift;
-
- my $ReturnPath = $head->get("Return-path") || "";
- return ( $ReturnPath =~ /<>/ );
-}
-
-
-=head2 MailError PARAM HASH
-
-Sends an error message. Takes a param hash:
-
-=over 4
-
-=item From - sender's address, by default is 'CorrespondAddress';
-
-=item To - recipient, by default is 'OwnerEmail';
-
-=item Bcc - optional Bcc recipients;
-
-=item Subject - subject of the message, default is 'There has been an error';
-
-=item Explanation - main content of the error, default value is 'Unexplained error';
-
-=item MIMEObj - optional MIME entity that's attached to the error mail, as well we
-add 'In-Reply-To' field to the error that points to this message.
-
-=item Attach - optional text that attached to the error as 'message/rfc822' part.
-
-=item LogLevel - log level under which we should write explanation message into the
-log, by default we log it as critical.
-
-=back
-
-=cut
+# {{{ sub MailError
sub MailError {
- my %args = (
- To => RT->Config->Get('OwnerEmail'),
- Bcc => undef,
- From => RT->Config->Get('CorrespondAddress'),
- Subject => 'There has been an error',
- Explanation => 'Unexplained error',
- MIMEObj => undef,
- Attach => undef,
- LogLevel => 'crit',
- @_
- );
-
- $RT::Logger->log(
- level => $args{'LogLevel'},
- message => $args{'Explanation'}
- ) if $args{'LogLevel'};
- # the colons are necessary to make ->build include non-standard headers
- my $entity = MIME::Entity->build(
- Type => "multipart/mixed",
- From => $args{'From'},
- Bcc => $args{'Bcc'},
- To => $args{'To'},
- Subject => $args{'Subject'},
- 'Precedence:' => 'bulk',
- 'X-RT-Loop-Prevention:' => RT->Config->Get('rtname'),
- );
- SetInReplyTo( Message => $entity, InReplyTo => $args{'MIMEObj'} );
-
- $entity->attach( Data => $args{'Explanation'} . "\n" );
-
- if ( $args{'MIMEObj'} ) {
- $args{'MIMEObj'}->sync_headers;
- $entity->add_part( $args{'MIMEObj'} );
- }
-
- if ( $args{'Attach'} ) {
- $entity->attach( Data => $args{'Attach'}, Type => 'message/rfc822' );
-
- }
-
- SendEmail( Entity => $entity, Bounce => 1 );
-}
-
-
-=head2 SendEmail Entity => undef, [ Bounce => 0, Ticket => undef, Transaction => undef ]
-
-Sends an email (passed as a L<MIME::Entity> object C<ENTITY>) using
-RT's outgoing mail configuration. If C<BOUNCE> is passed, and is a
-true value, the message will be marked as an autogenerated error, if
-possible. Sets Date field of the head to now if it's not set.
-
-Ticket and Transaction arguments are optional. If Transaction is
-specified and Ticket is not then ticket of the transaction is
-used, but only if the transaction belongs to a ticket.
-
-Returns 1 on success, 0 on error or -1 if message has no recipients
-and hasn't been sent.
-
-=head3 Signing and Encrypting
-
-This function as well signs and/or encrypts the message according to
-headers of a transaction's attachment or properties of a ticket's queue.
-To get full access to the configuration Ticket and/or Transaction
-arguments must be provided, but you can force behaviour using Sign
-and/or Encrypt arguments.
-
-The following precedence of arguments are used to figure out if
-the message should be encrypted and/or signed:
-
-* if Sign or Encrypt argument is defined then its value is used
-
-* else if Transaction's first attachment has X-RT-Sign or X-RT-Encrypt
-header field then it's value is used
-
-* else properties of a queue of the Ticket are used.
-
-=cut
-
-sub SendEmail {
- my (%args) = (
- Entity => undef,
- Bounce => 0,
- Ticket => undef,
- Transaction => undef,
- @_,
- );
-
- my $TicketObj = $args{'Ticket'};
- my $TransactionObj = $args{'Transaction'};
-
- foreach my $arg( qw(Entity Bounce) ) {
- next unless defined $args{ lc $arg };
-
- $RT::Logger->warning("'". lc($arg) ."' argument is deprecated, use '$arg' instead");
- $args{ $arg } = delete $args{ lc $arg };
- }
-
- unless ( $args{'Entity'} ) {
- $RT::Logger->crit( "Could not send mail without 'Entity' object" );
- return 0;
- }
-
- my $msgid = $args{'Entity'}->head->get('Message-ID') || '';
- chomp $msgid;
+ my %args = (To => $RT::OwnerEmail,
+ Bcc => undef,
+ From => $RT::CorrespondAddress,
+ Subject => 'There has been an error',
+ Explanation => 'Unexplained error',
+ MIMEObj => undef,
+ LogLevel => 'crit',
+ @_);
+
+
+ $RT::Logger->log(level => $args{'LogLevel'},
+ message => $args{'Explanation'}
+ );
+ my $entity = MIME::Entity->build( Type =>"multipart/mixed",
+ From => $args{'From'},
+ Bcc => $args{'Bcc'},
+ To => $args{'To'},
+ Subject => $args{'Subject'},
+ 'X-RT-Loop-Prevention' => $RT::rtname,
+ );
+
+ $entity->attach( Data => $args{'Explanation'}."\n");
- # If we don't have any recipients to send to, don't send a message;
- unless ( $args{'Entity'}->head->get('To')
- || $args{'Entity'}->head->get('Cc')
- || $args{'Entity'}->head->get('Bcc') )
- {
- $RT::Logger->info( $msgid . " No recipients found. Not sending." );
- return -1;
- }
-
- if ( $TransactionObj && !$TicketObj
- && $TransactionObj->ObjectType eq 'RT::Ticket' )
- {
- $TicketObj = $TransactionObj->Object;
- }
-
- if ( RT->Config->Get('GnuPG')->{'Enable'} ) {
- my %crypt;
-
- my $attachment;
- $attachment = $TransactionObj->Attachments->First
- if $TransactionObj;
-
- foreach my $argument ( qw(Sign Encrypt) ) {
- next if defined $args{ $argument };
-
- if ( $attachment && defined $attachment->GetHeader("X-RT-$argument") ) {
- $crypt{$argument} = $attachment->GetHeader("X-RT-$argument");
- } elsif ( $TicketObj ) {
- $crypt{$argument} = $TicketObj->QueueObj->$argument();
- }
- }
-
- my $res = SignEncrypt( %args, %crypt );
- return $res unless $res > 0;
- }
-
- unless ( $args{'Entity'}->head->get('Date') ) {
- require RT::Date;
- my $date = RT::Date->new( $RT::SystemUser );
- $date->SetToNow;
- $args{'Entity'}->head->set( 'Date', $date->RFC2822( Timezone => 'server' ) );
- }
-
- my $mail_command = RT->Config->Get('MailCommand');
-
- if ($mail_command eq 'testfile') {
- $Mail::Mailer::testfile::config{outfile} = File::Temp->new;
- }
-
- # if it is a sub routine, we just return it;
- return $mail_command->($args{'Entity'}) if UNIVERSAL::isa( $mail_command, 'CODE' );
-
- if ( $mail_command eq 'sendmailpipe' ) {
- my $path = RT->Config->Get('SendmailPath');
- my $args = RT->Config->Get('SendmailArguments');
-
- # SetOutgoingMailFrom
- if ( RT->Config->Get('SetOutgoingMailFrom') ) {
- my $OutgoingMailAddress;
-
- if ($TicketObj) {
- my $QueueName = $TicketObj->QueueObj->Name;
- my $QueueAddressOverride = RT->Config->Get('OverrideOutgoingMailFrom')->{$QueueName};
-
- if ($QueueAddressOverride) {
- $OutgoingMailAddress = $QueueAddressOverride;
- } else {
- $OutgoingMailAddress = $TicketObj->QueueObj->CorrespondAddress;
- }
- }
-
- $OutgoingMailAddress ||= RT->Config->Get('OverrideOutgoingMailFrom')->{'Default'};
-
- $args .= " -f $OutgoingMailAddress"
- if $OutgoingMailAddress;
- }
-
- # Set Bounce Arguments
- $args .= ' '. RT->Config->Get('SendmailBounceArguments') if $args{'Bounce'};
-
- # VERP
- if ( $TransactionObj and
- my $prefix = RT->Config->Get('VERPPrefix') and
- my $domain = RT->Config->Get('VERPDomain') )
- {
- my $from = $TransactionObj->CreatorObj->EmailAddress;
- $from =~ s/@/=/g;
- $from =~ s/\s//g;
- $args .= " -f $prefix$from\@$domain";
- }
-
- eval {
- # don't ignore CHLD signal to get proper exit code
- local $SIG{'CHLD'} = 'DEFAULT';
-
- open my $mail, "|$path $args" or die "couldn't execute program: $!";
-
- # if something wrong with $mail->print we will get PIPE signal, handle it
- local $SIG{'PIPE'} = sub { die "program unexpectedly closed pipe" };
- $args{'Entity'}->print($mail);
-
- unless ( close $mail ) {
- die "close pipe failed: $!" if $!; # system error
- # sendmail exit statuses mostly errors with data not software
- # TODO: status parsing: core dump, exit on signal or EX_*
- my $msg = "$msgid: `$path $args` exitted with code ". ($?>>8);
- $msg = ", interrupted by signal ". ($?&127) if $?&127;
- $RT::Logger->error( $msg );
- }
- };
- if ( $@ ) {
- $RT::Logger->crit( "$msgid: Could not send mail with command `$path $args`: " . $@ );
- return 0;
- }
+ my $mimeobj = $args{'MIMEObj'};
+ if ($mimeobj) {
+ $mimeobj->sync_headers();
+ $entity->add_part($mimeobj);
}
- elsif ( $mail_command eq 'smtp' ) {
- require Net::SMTP;
- my $smtp = do { local $@; eval { Net::SMTP->new(
- Host => RT->Config->Get('SMTPServer'),
- Debug => RT->Config->Get('SMTPDebug'),
- ) } };
- unless ( $smtp ) {
- $RT::Logger->crit( "Could not connect to SMTP server.");
- return 0;
- }
-
- # duplicate head as we want drop Bcc field
- my $head = $args{'Entity'}->head->dup;
- my @recipients = map $_->address, map
- Email::Address->parse($head->get($_)), qw(To Cc Bcc);
- $head->delete('Bcc');
-
- my $sender = RT->Config->Get('SMTPFrom')
- || $args{'Entity'}->head->get('From');
- chomp $sender;
-
- my $status = $smtp->mail( $sender )
- && $smtp->recipient( @recipients );
-
- if ( $status ) {
- $smtp->data;
- my $fh = $smtp->tied_fh;
- $head->print( $fh );
- print $fh "\n";
- $args{'Entity'}->print_body( $fh );
- $smtp->dataend;
- }
- $smtp->quit;
-
- unless ( $status ) {
- $RT::Logger->crit( "$msgid: Could not send mail via SMTP." );
- return 0;
- }
+
+ if ($RT::MailCommand eq 'sendmailpipe') {
+ open (MAIL, "|$RT::SendmailPath $RT::SendmailArguments") || return(0);
+ print MAIL $entity->as_string;
+ close(MAIL);
}
else {
- local ($ENV{'MAILADDRESS'}, $ENV{'PERL_MAILERS'});
-
- my @mailer_args = ($mail_command);
- if ( $mail_command eq 'sendmail' ) {
- $ENV{'PERL_MAILERS'} = RT->Config->Get('SendmailPath');
- push @mailer_args, split(/\s+/, RT->Config->Get('SendmailArguments'));
- }
- else {
- push @mailer_args, RT->Config->Get('MailParams');
- }
-
- unless ( $args{'Entity'}->send( @mailer_args ) ) {
- $RT::Logger->crit( "$msgid: Could not send mail." );
- return 0;
- }
- }
- return 1;
-}
-
-=head2 PrepareEmailUsingTemplate Template => '', Arguments => {}
-
-Loads a template. Parses it using arguments if it's not empty.
-Returns a tuple (L<RT::Template> object, error message).
-
-Note that even if a template object is returned MIMEObj method
-may return undef for empty templates.
-
-=cut
-
-sub PrepareEmailUsingTemplate {
- my %args = (
- Template => '',
- Arguments => {},
- @_
- );
-
- my $template = RT::Template->new( $RT::SystemUser );
- $template->LoadGlobalTemplate( $args{'Template'} );
- unless ( $template->id ) {
- return (undef, "Couldn't load template '". $args{'Template'} ."'");
- }
- return $template if $template->IsEmpty;
-
- my ($status, $msg) = $template->Parse( %{ $args{'Arguments'} } );
- return (undef, $msg) unless $status;
-
- return $template;
-}
-
-=head2 SendEmailUsingTemplate Template => '', Arguments => {}, From => CorrespondAddress, To => '', Cc => '', Bcc => ''
-
-Sends email using a template, takes name of template, arguments for it and recipients.
-
-=cut
-
-sub SendEmailUsingTemplate {
- my %args = (
- Template => '',
- Arguments => {},
- To => undef,
- Cc => undef,
- Bcc => undef,
- From => RT->Config->Get('CorrespondAddress'),
- InReplyTo => undef,
- @_
- );
-
- my ($template, $msg) = PrepareEmailUsingTemplate( %args );
- return (0, $msg) unless $template;
-
- my $mail = $template->MIMEObj;
- unless ( $mail ) {
- $RT::Logger->info("Message is not sent as template #". $template->id ." is empty");
- return -1;
+ $entity->send($RT::MailCommand, $RT::MailParams);
}
-
- $mail->head->set( $_ => $args{ $_ } )
- foreach grep defined $args{$_}, qw(To Cc Bcc From);
-
- SetInReplyTo( Message => $mail, InReplyTo => $args{'InReplyTo'} );
-
- return SendEmail( Entity => $mail );
}
-=head2 ForwardTransaction TRANSACTION, To => '', Cc => '', Bcc => ''
-
-Forwards transaction with all attachments as 'message/rfc822'.
-
-=cut
+# }}}
-sub ForwardTransaction {
- my $txn = shift;
- my %args = ( To => '', Cc => '', Bcc => '', @_ );
-
- my $entity = $txn->ContentAsMIME;
-
- return SendForward( %args, Entity => $entity, Transaction => $txn );
-}
-
-=head2 ForwardTicket TICKET, To => '', Cc => '', Bcc => ''
-
-Forwards a ticket's Create and Correspond Transactions and their Attachments as 'message/rfc822'.
-
-=cut
-
-sub ForwardTicket {
- my $ticket = shift;
- my %args = ( To => '', Cc => '', Bcc => '', @_ );
-
- my $txns = $ticket->Transactions;
- $txns->Limit(
- FIELD => 'Type',
- VALUE => $_,
- ) for qw(Create Correspond);
-
- my $entity = MIME::Entity->build(
- Type => 'multipart/mixed',
- );
- $entity->add_part( $_ ) foreach
- map $_->ContentAsMIME,
- @{ $txns->ItemsArrayRef };
-
- return SendForward( %args, Entity => $entity, Ticket => $ticket, Template => 'Forward Ticket' );
-}
-
-=head2 SendForward Entity => undef, Ticket => undef, Transaction => undef, Template => undef, To => '', Cc => '', Bcc => ''
-
-Forwards an Entity representing Ticket or Transaction as 'message/rfc822'. Entity is wrapped into Template.
-
-=cut
-
-sub SendForward {
- my (%args) = (
- Entity => undef,
- Ticket => undef,
- Transaction => undef,
- Template => 'Forward',
- To => '', Cc => '', Bcc => '',
- @_
- );
-
- my $txn = $args{'Transaction'};
- my $ticket = $args{'Ticket'};
- $ticket ||= $txn->Object if $txn;
-
- my $entity = $args{'Entity'};
- unless ( $entity ) {
- require Carp;
- $RT::Logger->error(Carp::longmess("No entity provided"));
- return (0, $ticket->loc("Couldn't send email"));
- }
-
- my ($template, $msg) = PrepareEmailUsingTemplate(
- Template => $args{'Template'},
- Arguments => {
- Ticket => $ticket,
- Transaction => $txn,
- },
- );
-
- my $mail;
- if ( $template ) {
- $mail = $template->MIMEObj;
- } else {
- $RT::Logger->warning($msg);
- }
- unless ( $mail ) {
- $RT::Logger->warning("Couldn't generate email using template '$args{Template}'");
-
- my $description;
- unless ( $args{'Transaction'} ) {
- $description = 'This is forward of ticket #'. $ticket->id;
- } else {
- $description = 'This is forward of transaction #'
- . $txn->id ." of a ticket #". $txn->ObjectId;
- }
- $mail = MIME::Entity->build(
- Type => 'text/plain',
- Data => $description,
- );
- }
-
- $mail->head->set( $_ => EncodeToMIME( String => $args{$_} ) )
- foreach grep defined $args{$_}, qw(To Cc Bcc);
-
- $mail->attach(
- Type => 'message/rfc822',
- Disposition => 'attachment',
- Description => 'forwarded message',
- Data => $entity->as_string,
- );
-
- my $from;
- my $subject = '';
- $subject = $txn->Subject if $txn;
- $subject ||= $ticket->Subject if $ticket;
- if ( RT->Config->Get('ForwardFromUser') ) {
- $from = ($txn || $ticket)->CurrentUser->UserObj->EmailAddress;
- } else {
- # XXX: what if want to forward txn of other object than ticket?
- $subject = AddSubjectTag( $subject, $ticket );
- $from = $ticket->QueueObj->CorrespondAddress
- || RT->Config->Get('CorrespondAddress');
- }
- $mail->head->set( Subject => EncodeToMIME( String => "Fwd: $subject" ) );
- $mail->head->set( From => EncodeToMIME( String => $from ) );
-
- my $status = RT->Config->Get('ForwardFromUser')
- # never sign if we forward from User
- ? SendEmail( %args, Entity => $mail, Sign => 0 )
- : SendEmail( %args, Entity => $mail );
- return (0, $ticket->loc("Couldn't send email")) unless $status;
- return (1, $ticket->loc("Send email successfully"));
-}
-
-=head2 SignEncrypt Entity => undef, Sign => 0, Encrypt => 0
-
-Signs and encrypts message using L<RT::Crypt::GnuPG>, but as well
-handle errors with users' keys.
-
-If a recipient has no key or has other problems with it, then the
-unction sends a error to him using 'Error: public key' template.
-Also, notifies RT's owner using template 'Error to RT owner: public key'
-to inform that there are problems with users' keys. Then we filter
-all bad recipients and retry.
-
-Returns 1 on success, 0 on error and -1 if all recipients are bad and
-had been filtered out.
-
-=cut
-
-sub SignEncrypt {
- my %args = (
- Entity => undef,
- Sign => 0,
- Encrypt => 0,
- @_
- );
- return 1 unless $args{'Sign'} || $args{'Encrypt'};
-
- my $msgid = $args{'Entity'}->head->get('Message-ID') || '';
- chomp $msgid;
-
- $RT::Logger->debug("$msgid Signing message") if $args{'Sign'};
- $RT::Logger->debug("$msgid Encrypting message") if $args{'Encrypt'};
-
- require RT::Crypt::GnuPG;
- my %res = RT::Crypt::GnuPG::SignEncrypt( %args );
- return 1 unless $res{'exit_code'};
-
- my @status = RT::Crypt::GnuPG::ParseStatus( $res{'status'} );
-
- my @bad_recipients;
- foreach my $line ( @status ) {
- # if the passphrase fails, either you have a bad passphrase
- # or gpg-agent has died. That should get caught in Create and
- # Update, but at least throw an error here
- if (($line->{'Operation'}||'') eq 'PassphraseCheck'
- && $line->{'Status'} =~ /^(?:BAD|MISSING)$/ ) {
- $RT::Logger->error( "$line->{'Status'} PASSPHRASE: $line->{'Message'}" );
- return 0;
- }
- next unless ($line->{'Operation'}||'') eq 'RecipientsCheck';
- next if $line->{'Status'} eq 'DONE';
- $RT::Logger->error( $line->{'Message'} );
- push @bad_recipients, $line;
- }
- return 0 unless @bad_recipients;
-
- $_->{'AddressObj'} = (Email::Address->parse( $_->{'Recipient'} ))[0]
- foreach @bad_recipients;
-
- foreach my $recipient ( @bad_recipients ) {
- my $status = SendEmailUsingTemplate(
- To => $recipient->{'AddressObj'}->address,
- Template => 'Error: public key',
- Arguments => {
- %$recipient,
- TicketObj => $args{'Ticket'},
- TransactionObj => $args{'Transaction'},
- },
- );
- unless ( $status ) {
- $RT::Logger->error("Couldn't send 'Error: public key'");
- }
- }
-
- my $status = SendEmailUsingTemplate(
- To => RT->Config->Get('OwnerEmail'),
- Template => 'Error to RT owner: public key',
- Arguments => {
- BadRecipients => \@bad_recipients,
- TicketObj => $args{'Ticket'},
- TransactionObj => $args{'Transaction'},
- },
- );
- unless ( $status ) {
- $RT::Logger->error("Couldn't send 'Error to RT owner: public key'");
- }
-
- DeleteRecipientsFromHead(
- $args{'Entity'}->head,
- map $_->{'AddressObj'}->address, @bad_recipients
- );
-
- unless ( $args{'Entity'}->head->get('To')
- || $args{'Entity'}->head->get('Cc')
- || $args{'Entity'}->head->get('Bcc') )
- {
- $RT::Logger->debug("$msgid No recipients that have public key, not sending");
- return -1;
- }
-
- # redo without broken recipients
- %res = RT::Crypt::GnuPG::SignEncrypt( %args );
- return 0 if $res{'exit_code'};
-
- return 1;
-}
-
-use MIME::Words ();
-
-=head2 EncodeToMIME
-
-Takes a hash with a String and a Charset. Returns the string encoded
-according to RFC2047, using B (base64 based) encoding.
-
-String must be a perl string, octets are returned.
-
-If Charset is not provided then $EmailOutputEncoding config option
-is used, or "latin-1" if that is not set.
-
-=cut
-
-sub EncodeToMIME {
- my %args = (
- String => undef,
- Charset => undef,
- @_
- );
- my $value = $args{'String'};
- return $value unless $value; # 0 is perfect ascii
- my $charset = $args{'Charset'} || RT->Config->Get('EmailOutputEncoding');
- my $encoding = 'B';
-
- # using RFC2047 notation, sec 2.
- # encoded-word = "=?" charset "?" encoding "?" encoded-text "?="
-
- # An 'encoded-word' may not be more than 75 characters long
- #
- # MIME encoding increases 4/3*(number of bytes), and always in multiples
- # of 4. Thus we have to find the best available value of bytes available
- # for each chunk.
- #
- # First we get the integer max which max*4/3 would fit on space.
- # Then we find the greater multiple of 3 lower or equal than $max.
- my $max = int(
- ( ( 75 - length( '=?' . $charset . '?' . $encoding . '?' . '?=' ) )
- * 3
- ) / 4
- );
- $max = int( $max / 3 ) * 3;
-
- chomp $value;
-
- if ( $max <= 0 ) {
-
- # gives an error...
- $RT::Logger->crit("Can't encode! Charset or encoding too big.");
- return ($value);
- }
-
- return ($value) unless $value =~ /[^\x20-\x7e]/;
-
- $value =~ s/\s+$//;
-
- # we need perl string to split thing char by char
- Encode::_utf8_on($value) unless Encode::is_utf8($value);
-
- my ( $tmp, @chunks ) = ( '', () );
- while ( length $value ) {
- my $char = substr( $value, 0, 1, '' );
- my $octets = Encode::encode( $charset, $char );
- if ( length($tmp) + length($octets) > $max ) {
- push @chunks, $tmp;
- $tmp = '';
- }
- $tmp .= $octets;
- }
- push @chunks, $tmp if length $tmp;
-
- # encode an join chuncks
- $value = join "\n ",
- map MIME::Words::encode_mimeword( $_, $encoding, $charset ),
- @chunks;
- return ($value);
-}
+# {{{ Create User
sub CreateUser {
- my ( $Username, $Address, $Name, $ErrorsTo, $entity ) = @_;
-
- my $NewUser = RT::User->new( $RT::SystemUser );
-
- my ( $Val, $Message ) = $NewUser->Create(
- Name => ( $Username || $Address ),
- EmailAddress => $Address,
- RealName => $Name,
- Password => undef,
- Privileged => 0,
- Comments => 'Autocreated on ticket submission',
- );
-
+ my ($Username, $Address, $Name, $ErrorsTo, $entity) = @_;
+ my $NewUser = RT::User->new($RT::SystemUser);
+
+ # This data is tainted by some Very Broken mailers.
+ # (Sometimes they send raw ISO 8859-1 data here. fear that.
+ require Encode;
+ $Username = Encode::encode(utf8 => $Username, Encode::FB_PERLQQ()) if defined $Username;
+ $Name = Encode::encode(utf8 => $Name, Encode::FB_PERLQQ()) if defined $Name;
+
+ my ($Val, $Message) =
+ $NewUser->Create(Name => ($Username || $Address),
+ EmailAddress => $Address,
+ RealName => $Name,
+ Password => undef,
+ Privileged => 0,
+ Comments => 'Autocreated on ticket submission'
+ );
+
unless ($Val) {
-
+
# Deal with the race condition of two account creations at once
+ #
if ($Username) {
$NewUser->LoadByName($Username);
}
-
- unless ( $NewUser->Id ) {
+
+ unless ($NewUser->Id) {
$NewUser->LoadByEmail($Address);
}
-
- unless ( $NewUser->Id ) {
- MailError(
- To => $ErrorsTo,
- Subject => "User could not be created",
- Explanation =>
- "User creation failed in mailgateway: $Message",
- MIMEObj => $entity,
- LogLevel => 'crit',
- );
+
+ unless ($NewUser->Id) {
+ MailError( To => $ErrorsTo,
+ Subject => "User could not be created",
+ Explanation => "User creation failed in mailgateway: $Message",
+ MIMEObj => $entity,
+ LogLevel => 'crit'
+ );
}
}
#Load the new user object
- my $CurrentUser = new RT::CurrentUser;
- $CurrentUser->LoadByEmail( $Address );
+ my $CurrentUser = RT::CurrentUser->new();
+ $CurrentUser->LoadByEmail($Address);
- unless ( $CurrentUser->id ) {
- $RT::Logger->warning(
- "Couldn't load user '$Address'." . "giving up" );
- MailError(
- To => $ErrorsTo,
- Subject => "User could not be loaded",
- Explanation =>
- "User '$Address' could not be loaded in the mail gateway",
- MIMEObj => $entity,
- LogLevel => 'crit'
- );
+ unless ($CurrentUser->id) {
+ $RT::Logger->warning("Couldn't load user '$Address'.". "giving up");
+ MailError( To => $ErrorsTo,
+ Subject => "User could not be loaded",
+ Explanation => "User '$Address' could not be loaded in the mail gateway",
+ MIMEObj => $entity,
+ LogLevel => 'crit'
+ );
}
return $CurrentUser;
}
+# }}}
+# {{{ ParseCcAddressesFromHead
+=head2 ParseCcAddressesFromHead HASHREF
-
-=head2 ParseCcAddressesFromHead HASH
-
-Takes a hash containing QueueObj, Head and CurrentUser objects.
-Returns a list of all email addresses in the To and Cc
-headers b<except> the current Queue\'s email addresses, the CurrentUser\'s
+Takes a hashref object containing QueueObj, Head and CurrentUser objects.
+Returns a list of all email addresses in the To and Cc
+headers b<except> the current Queue\'s email addresses, the CurrentUser\'s
email address and anything that the configuration sub RT::IsRTAddress matches.
=cut
-
+
sub ParseCcAddressesFromHead {
- my %args = (
- Head => undef,
- QueueObj => undef,
- CurrentUser => undef,
- @_
- );
-
- my @recipients =
- map lc $_->address,
- map Email::Address->parse( $args{'Head'}->get( $_ ) ),
- qw(To Cc);
-
- my @res;
- foreach my $address ( @recipients ) {
- $address = $args{'CurrentUser'}->UserObj->CanonicalizeEmailAddress( $address );
- next if lc $args{'CurrentUser'}->EmailAddress eq $address;
- next if lc $args{'QueueObj'}->CorrespondAddress eq $address;
- next if lc $args{'QueueObj'}->CommentAddress eq $address;
- next if RT::EmailParser->IsRTAddress( $address );
-
- push @res, $address;
- }
- return @res;
+ my %args = ( Head => undef,
+ QueueObj => undef,
+ CurrentUser => undef,
+ @_ );
+
+ my (@Addresses);
+
+ my @ToObjs = Mail::Address->parse($args{'Head'}->get('To'));
+ my @CcObjs = Mail::Address->parse($args{'Head'}->get('Cc'));
+
+ foreach my $AddrObj (@ToObjs, @CcObjs) {
+ my $Address = $AddrObj->address;
+ $Address = $args{'CurrentUser'}->UserObj->CanonicalizeEmailAddress($Address);
+ next if ($args{'CurrentUser'}->EmailAddress =~ /^$Address$/i);
+ next if ($args{'QueueObj'}->CorrespondAddress =~ /^$Address$/i);
+ next if ($args{'QueueObj'}->CommentAddress =~ /^$Address$/i);
+ next if (RT::EmailParser::IsRTAddress(undef, $Address));
+
+ push (@Addresses, $Address);
+ }
+ return (@Addresses);
}
+# }}}
+
+# {{{ ParseSenderAdddressFromHead
-=head2 ParseSenderAddressFromHead HEAD
+=head2 ParseSenderAddressFromHead
-Takes a MIME::Header object. Returns a tuple: (user@host, friendly name)
+Takes a MIME::Header object. Returns a tuple: (user@host, friendly name)
of the From (evaluated in order of Reply-To:, From:, Sender)
=cut
sub ParseSenderAddressFromHead {
my $head = shift;
-
#Figure out who's sending this message.
- foreach my $header ('Reply-To', 'From', 'Sender') {
- my $addr_line = $head->get($header) || next;
- my ($addr, $name) = ParseAddressFromHeader( $addr_line );
- # only return if the address is not empty
- return ($addr, $name) if $addr;
- }
-
- return (undef, undef);
+ my $From = $head->get('Reply-To') ||
+ $head->get('From') ||
+ $head->get('Sender');
+ return (ParseAddressFromHeader($From));
}
+# }}}
+
+# {{{ ParseErrorsToAdddressFromHead
-=head2 ParseErrorsToAddressFromHead HEAD
+=head2 ParseErrorsToAddressFromHead
Takes a MIME::Header object. Return a single value : user@host
-of the From (evaluated in order of Return-path:,Errors-To:,Reply-To:,
-From:, Sender)
+of the From (evaluated in order of Errors-To:,Reply-To:, From:, Sender)
=cut
sub ParseErrorsToAddressFromHead {
my $head = shift;
-
#Figure out who's sending this message.
- foreach my $header ( 'Errors-To', 'Reply-To', 'From', 'Sender' ) {
-
- # If there's a header of that name
- my $headerobj = $head->get($header);
- if ($headerobj) {
- my ( $addr, $name ) = ParseAddressFromHeader($headerobj);
-
- # If it's got actual useful content...
- return ($addr) if ($addr);
- }
+ foreach my $header ('Errors-To' , 'Reply-To', 'From', 'Sender' ) {
+ # If there's a header of that name
+ my $headerobj = $head->get($header);
+ if ($headerobj) {
+ my ($addr, $name ) = ParseAddressFromHeader($headerobj);
+ # If it's got actual useful content...
+ return ($addr) if ($addr);
+ }
}
}
+# }}}
-
+# {{{ ParseAddressFromHeader
=head2 ParseAddressFromHeader ADDRESS
-Takes an address from C<$head->get('Line')> and returns a tuple: user@host, friendly name
+Takes an address from $head->get('Line') and returns a tuple: user@host, friendly name
=cut
-sub ParseAddressFromHeader {
- my $Addr = shift;
- # Some broken mailers send: ""Vincent, Jesse"" <jesse@fsck.com>. Hate
- $Addr =~ s/\"\"(.*?)\"\"/\"$1\"/g;
- my @Addresses = RT::EmailParser->ParseEmailAddress($Addr);
+sub ParseAddressFromHeader{
+ my $Addr = shift;
+
+ my @Addresses = Mail::Address->parse($Addr);
+
+ my $AddrObj = $Addresses[0];
- my ($AddrObj) = grep ref $_, @Addresses;
- unless ( $AddrObj ) {
- return ( undef, undef );
+ unless (ref($AddrObj)) {
+ return(undef,undef);
}
-
- my $Name = ( $AddrObj->name || $AddrObj->phrase || $AddrObj->comment || $AddrObj->address );
-
+
+ my $Name = ($AddrObj->phrase || $AddrObj->comment || $AddrObj->address);
+
#Lets take the from and load a user object.
my $Address = $AddrObj->address;
- return ( $Address, $Name );
-}
-
-=head2 DeleteRecipientsFromHead HEAD RECIPIENTS
-
-Gets a head object and list of addresses.
-Deletes addresses from To, Cc or Bcc fields.
-
-=cut
-
-sub DeleteRecipientsFromHead {
- my $head = shift;
- my %skip = map { lc $_ => 1 } @_;
-
- foreach my $field ( qw(To Cc Bcc) ) {
- $head->set( $field =>
- join ', ', map $_->format, grep !$skip{ lc $_->address },
- Email::Address->parse( $head->get( $field ) )
- );
- }
-}
-
-sub GenMessageId {
- my %args = (
- Ticket => undef,
- Scrip => undef,
- ScripAction => undef,
- @_
- );
- my $org = RT->Config->Get('Organization');
- my $ticket_id = ( ref $args{'Ticket'}? $args{'Ticket'}->id : $args{'Ticket'} ) || 0;
- my $scrip_id = ( ref $args{'Scrip'}? $args{'Scrip'}->id : $args{'Scrip'} ) || 0;
- my $sent = ( ref $args{'ScripAction'}? $args{'ScripAction'}->{'_Message_ID'} : 0 ) || 0;
-
- return "<rt-". $RT::VERSION ."-". $$ ."-". CORE::time() ."-". int(rand(2000)) .'.'
- . $ticket_id ."-". $scrip_id ."-". $sent ."@". $org .">" ;
+ return ($Address, $Name);
}
+# }}}
-sub SetInReplyTo {
- my %args = (
- Message => undef,
- InReplyTo => undef,
- Ticket => undef,
- @_
- );
- return unless $args{'Message'} && $args{'InReplyTo'};
-
- my $get_header = sub {
- my @res;
- if ( $args{'InReplyTo'}->isa('MIME::Entity') ) {
- @res = $args{'InReplyTo'}->head->get( shift );
- } else {
- @res = $args{'InReplyTo'}->GetHeader( shift ) || '';
- }
- return grep length, map { split /\s+/m, $_ } grep defined, @res;
- };
-
- my @id = $get_header->('Message-ID');
- #XXX: custom header should begin with X- otherwise is violation of the standard
- my @rtid = $get_header->('RT-Message-ID');
- my @references = $get_header->('References');
- unless ( @references ) {
- @references = $get_header->('In-Reply-To');
- }
- push @references, @id, @rtid;
- if ( $args{'Ticket'} ) {
- my $pseudo_ref = '<RT-Ticket-'. $args{'Ticket'}->id .'@'. RT->Config->Get('Organization') .'>';
- push @references, $pseudo_ref unless grep $_ eq $pseudo_ref, @references;
- }
- @references = splice @references, 4, -6
- if @references > 10;
-
- my $mail = $args{'Message'};
- $mail->head->set( 'In-Reply-To' => join ' ', @rtid? (@rtid) : (@id) ) if @id || @rtid;
- $mail->head->set( 'References' => join ' ', @references );
-}
-
-sub ParseTicketId {
- my $Subject = shift;
- my $rtname = RT->Config->Get('rtname');
- my $test_name = RT->Config->Get('EmailSubjectTagRegex') || qr/\Q$rtname\E/i;
-
- my $id;
- if ( $Subject =~ s/\[$test_name\s+\#(\d+)\s*\]//i ) {
- $id = $1;
- } else {
- foreach my $tag ( RT->System->SubjectTag ) {
- next unless $Subject =~ s/\[\Q$tag\E\s+\#(\d+)\s*\]//i;
- $id = $1;
- last;
- }
- }
- return undef unless $id;
-
- $RT::Logger->debug("Found a ticket ID. It's $id");
- return $id;
-}
-
-sub AddSubjectTag {
- my $subject = shift;
- my $ticket = shift;
- unless ( ref $ticket ) {
- my $tmp = RT::Ticket->new( $RT::SystemUser );
- $tmp->Load( $ticket );
- $ticket = $tmp;
- }
- my $id = $ticket->id;
- my $queue_tag = $ticket->QueueObj->SubjectTag;
-
- my $tag_re = RT->Config->Get('EmailSubjectTagRegex');
- unless ( $tag_re ) {
- my $tag = $queue_tag || RT->Config->Get('rtname');
- $tag_re = qr/\Q$tag\E/;
- } elsif ( $queue_tag ) {
- $tag_re = qr/$tag_re|\Q$queue_tag\E/;
- }
- return $subject if $subject =~ /\[$tag_re\s+#$id\]/;
-
- $subject =~ s/(\r\n|\n|\s)/ /gi;
- chomp $subject;
- return "[". ($queue_tag || RT->Config->Get('rtname')) ." #$id] $subject";
-}
-
-
-=head2 Gateway ARGSREF
-
-
-Takes parameters:
-
- action
- queue
- message
+=head2 Gateway
This performs all the "guts" of the mail rt-mailgate program, and is
designed to be called from the web interface with a message, user
object, and so on.
-Can also take an optional 'ticket' parameter; this ticket id overrides
-any ticket id found in the subject.
-
-Returns:
-
- An array of:
-
- (status code, message, optional ticket object)
-
- status code is a numeric value.
-
- for temporary failures, the status code should be -75
-
- for permanent failures which are handled by RT, the status code
- should be 0
-
- for succces, the status code should be 1
-
-
-
=cut
-sub _LoadPlugins {
- my @mail_plugins = @_;
-
- my @res;
- foreach my $plugin (@mail_plugins) {
- if ( ref($plugin) eq "CODE" ) {
- push @res, $plugin;
- } elsif ( !ref $plugin ) {
- my $Class = $plugin;
- $Class = "RT::Interface::Email::" . $Class
- unless $Class =~ /^RT::Interface::Email::/;
- $Class->require or
- do { $RT::Logger->error("Couldn't load $Class: $@"); next };
-
- no strict 'refs';
- unless ( defined *{ $Class . "::GetCurrentUser" }{CODE} ) {
- $RT::Logger->crit( "No GetCurrentUser code found in $Class module");
- next;
- }
- push @res, $Class;
- } else {
- $RT::Logger->crit( "$plugin - is not class name or code reference");
- }
- }
- return @res;
-}
-
sub Gateway {
- my $argsref = shift;
- my %args = (
- action => 'correspond',
- queue => '1',
- ticket => undef,
- message => undef,
- %$argsref
- );
-
- my $SystemTicket;
- my $Right;
+ my %args = ( message => undef,
+ queue => 1,
+ action => 'correspond',
+ ticket => undef,
+ @_ );
# Validate the action
- my ( $status, @actions ) = IsCorrectAction( $args{'action'} );
- unless ($status) {
- return (
- -75,
- "Invalid 'action' parameter "
- . $actions[0]
- . " for queue "
- . $args{'queue'},
- undef
- );
+ unless ( $args{'action'} =~ /^(comment|correspond|action)$/ ) {
+
+ # Can't safely loc this. What object do we loc around?
+ return ( 0, "Invalid 'action' parameter", undef );
}
my $parser = RT::EmailParser->new();
- $parser->SmartParseMIMEEntityFromScalar(
- Message => $args{'message'},
- Decode => 0,
- Exact => 1,
- );
+ $parser->ParseMIMEEntityFromScalar( $args{'message'} );
my $Message = $parser->Entity();
- unless ($Message) {
- MailError(
- Subject => "RT Bounce: Unparseable message",
- Explanation => "RT couldn't process the message below",
- Attach => $args{'message'}
- );
-
- return ( 0,
- "Failed to parse this message. Something is likely badly wrong with the message"
- );
- }
-
- my @mail_plugins = grep $_, RT->Config->Get('MailPlugins');
- push @mail_plugins, "Auth::MailFrom" unless @mail_plugins;
- @mail_plugins = _LoadPlugins( @mail_plugins );
-
- my %skip_plugin;
- foreach my $class( grep !ref, @mail_plugins ) {
- # check if we should apply filter before decoding
- my $check_cb = do {
- no strict 'refs';
- *{ $class . "::ApplyBeforeDecode" }{CODE};
- };
- next unless defined $check_cb;
- next unless $check_cb->(
- Message => $Message,
- RawMessageRef => \$args{'message'},
- );
-
- $skip_plugin{ $class }++;
+ my $head = $Message->head;
- my $Code = do {
- no strict 'refs';
- *{ $class . "::GetCurrentUser" }{CODE};
- };
- my ($status, $msg) = $Code->(
- Message => $Message,
- RawMessageRef => \$args{'message'},
- );
- next if $status > 0;
-
- if ( $status == -2 ) {
- return (1, $msg, undef);
- } elsif ( $status == -1 ) {
- return (0, $msg, undef);
- }
- }
- @mail_plugins = grep !$skip_plugin{"$_"}, @mail_plugins;
- $parser->_DecodeBodies;
- $parser->_PostProcessNewEntity;
+ my ( $CurrentUser, $AuthStat, $status, $error );
- my $head = $Message->head;
- my $ErrorsTo = ParseErrorsToAddressFromHead( $head );
+ my $ErrorsTo = ParseErrorsToAddressFromHead($head);
- my $MessageId = $head->get('Message-ID')
- || "<no-message-id-". time . rand(2000) .'@'. RT->Config->Get('Organization') .'>';
+ my $MessageId = $head->get('Message-Id')
+ || "<no-message-id-" . time . rand(2000) . "\@.$RT::Organization>";
#Pull apart the subject line
my $Subject = $head->get('Subject') || '';
chomp $Subject;
-
- # {{{ Lets check for mail loops of various sorts.
- my ($should_store_machine_generated_message, $IsALoop, $result);
- ( $should_store_machine_generated_message, $ErrorsTo, $result, $IsALoop ) =
- _HandleMachineGeneratedMail(
- Message => $Message,
- ErrorsTo => $ErrorsTo,
- Subject => $Subject,
- MessageId => $MessageId
- );
-
- # Do not pass loop messages to MailPlugins, to make sure the loop
- # is broken, unless $RT::StoreLoops is set.
- if ($IsALoop && !$should_store_machine_generated_message) {
- return ( 0, $result, undef );
- }
- # }}}
- $args{'ticket'} ||= ParseTicketId( $Subject );
- $SystemTicket = RT::Ticket->new( $RT::SystemUser );
- $SystemTicket->Load( $args{'ticket'} ) if ( $args{'ticket'} ) ;
- if ( $SystemTicket->id ) {
- $Right = 'ReplyToTicket';
- } else {
- $Right = 'CreateTicket';
+ $args{'ticket'} ||= $parser->ParseTicketId($Subject);
+
+ my $SystemTicket;
+ if ($args{'ticket'} ) {
+ $SystemTicket = RT::Ticket->new($RT::SystemUser);
+ $SystemTicket->Load($args{'ticket'});
}
#Set up a queue object
- my $SystemQueueObj = RT::Queue->new( $RT::SystemUser );
+ my $SystemQueueObj = RT::Queue->new($RT::SystemUser);
$SystemQueueObj->Load( $args{'queue'} );
- # We can safely have no queue of we have a known-good ticket
- unless ( $SystemTicket->id || $SystemQueueObj->id ) {
- return ( -75, "RT couldn't find the queue: " . $args{'queue'}, undef );
- }
-
- my ($AuthStat, $CurrentUser, $error) = GetAuthenticationLevel(
- MailPlugins => \@mail_plugins,
- Actions => \@actions,
- Message => $Message,
- RawMessageRef => \$args{message},
- SystemTicket => $SystemTicket,
- SystemQueue => $SystemQueueObj,
- );
-
- # {{{ If authentication fails and no new user was created, get out.
- if ( !$CurrentUser || !$CurrentUser->id || $AuthStat == -1 ) {
-
- # If the plugins refused to create one, they lose.
- unless ( $AuthStat == -1 ) {
- _NoAuthorizedUserFound(
- Right => $Right,
- Message => $Message,
- Requestor => $ErrorsTo,
- Queue => $args{'queue'}
- );
- }
- return ( 0, "Could not load a valid user", undef );
- }
-
- # If we got a user, but they don't have the right to say things
- if ( $AuthStat == 0 ) {
+ # We can safely have no queue of we have a known-good ticket
+ unless ( $args{'ticket'} || $SystemQueueObj->id ) {
MailError(
- To => $ErrorsTo,
- Subject => "Permission Denied",
- Explanation =>
- "You do not have permission to communicate with RT",
- MIMEObj => $Message
- );
- return (
- 0,
- "$ErrorsTo tried to submit a message to "
- . $args{'Queue'}
- . " without permission.",
- undef
- );
- }
-
-
- unless ($should_store_machine_generated_message) {
- return ( 0, $result, undef );
+ To => $RT::OwnerEmail,
+ Subject => "RT Bounce: $Subject",
+ Explanation => "RT couldn't find the queue: " . $args{'queue'},
+ MIMEObj => $Message );
+ return ( 0, "RT couldn't find the queue: " . $args{'queue'}, undef );
}
- # if plugin's updated SystemTicket then update arguments
- $args{'ticket'} = $SystemTicket->Id if $SystemTicket && $SystemTicket->Id;
-
- my $Ticket = RT::Ticket->new($CurrentUser);
-
- if ( !$args{'ticket'} && grep /^(comment|correspond)$/, @actions )
- {
-
- my @Cc;
- my @Requestors = ( $CurrentUser->id );
-
- if (RT->Config->Get('ParseNewMessageForTicketCcs')) {
- @Cc = ParseCcAddressesFromHead(
- Head => $head,
- CurrentUser => $CurrentUser,
- QueueObj => $SystemQueueObj
- );
- }
-
- my ( $id, $Transaction, $ErrStr ) = $Ticket->Create(
- Queue => $SystemQueueObj->Id,
- Subject => $Subject,
- Requestor => \@Requestors,
- Cc => \@Cc,
- MIMEObj => $Message
- );
- if ( $id == 0 ) {
- MailError(
- To => $ErrorsTo,
- Subject => "Ticket creation failed: $Subject",
- Explanation => $ErrStr,
- MIMEObj => $Message
- );
- return ( 0, "Ticket creation failed: $ErrStr", $Ticket );
- }
-
- # strip comments&corresponds from the actions we don't need
- # to record them if we've created the ticket just now
- @actions = grep !/^(comment|correspond)$/, @actions;
- $args{'ticket'} = $id;
-
- } elsif ( $args{'ticket'} ) {
-
- $Ticket->Load( $args{'ticket'} );
- unless ( $Ticket->Id ) {
- my $error = "Could not find a ticket with id " . $args{'ticket'};
- MailError(
- To => $ErrorsTo,
- Subject => "Message not recorded: $Subject",
- Explanation => $error,
- MIMEObj => $Message
- );
-
- return ( 0, $error );
- }
- $args{'ticket'} = $Ticket->id;
- } else {
- return ( 1, "Success", $Ticket );
- }
-
- # }}}
-
- my $unsafe_actions = RT->Config->Get('UnsafeEmailCommands');
- foreach my $action (@actions) {
-
- # If the action is comment, add a comment.
- if ( $action =~ /^(?:comment|correspond)$/i ) {
- my $method = ucfirst lc $action;
- my ( $status, $msg ) = $Ticket->$method( MIMEObj => $Message );
- unless ($status) {
-
- #Warn the sender that we couldn't actually submit the comment.
- MailError(
- To => $ErrorsTo,
- Subject => "Message not recorded: $Subject",
- Explanation => $msg,
- MIMEObj => $Message
- );
- return ( 0, "Message not recorded: $msg", $Ticket );
- }
- } elsif ($unsafe_actions) {
- my ( $status, $msg ) = _RunUnsafeAction(
- Action => $action,
- ErrorsTo => $ErrorsTo,
- Message => $Message,
- Ticket => $Ticket,
- CurrentUser => $CurrentUser,
- );
- return ($status, $msg, $Ticket) unless $status == 1;
- }
- }
- return ( 1, "Success", $Ticket );
-}
-
-=head2 GetAuthenticationLevel
-
# Authentication Level
- # -1 - Get out. this user has been explicitly declined
+ # -1 - Get out. this user has been explicitly declined
# 0 - User may not do anything (Not used at the moment)
# 1 - Normal user
# 2 - User is allowed to specify status updates etc. a la enhanced-mailgate
-=cut
-
-sub GetAuthenticationLevel {
- my %args = (
- MailPlugins => [],
- Actions => [],
- Message => undef,
- RawMessageRef => undef,
- SystemTicket => undef,
- SystemQueue => undef,
- @_,
- );
-
- my ( $CurrentUser, $AuthStat, $error );
-
- # Initalize AuthStat so comparisons work correctly
- $AuthStat = -9999999;
-
- # if plugin returns AuthStat -2 we skip action
- # NOTE: this is experimental API and it would be changed
- my %skip_action = ();
-
+ push @RT::MailPlugins, "Auth::MailFrom" unless @RT::MailPlugins;
# Since this needs loading, no matter what
- foreach (@{ $args{MailPlugins} }) {
- my ($Code, $NewAuthStat);
+
+ for (@RT::MailPlugins) {
+ my $Code;
+ my $NewAuthStat;
if ( ref($_) eq "CODE" ) {
$Code = $_;
- } else {
- no strict 'refs';
- $Code = *{ $_ . "::GetCurrentUser" }{CODE};
}
-
- foreach my $action (@{ $args{Actions} }) {
- ( $CurrentUser, $NewAuthStat ) = $Code->(
- Message => $args{Message},
- RawMessageRef => $args{RawMessageRef},
- CurrentUser => $CurrentUser,
- AuthLevel => $AuthStat,
- Action => $action,
- Ticket => $args{SystemTicket},
- Queue => $args{SystemQueue},
- );
-
-# You get the highest level of authentication you were assigned, unless you get the magic -1
-# If a module returns a "-1" then we discard the ticket, so.
- $AuthStat = $NewAuthStat
- if ( $NewAuthStat > $AuthStat or $NewAuthStat == -1 or $NewAuthStat == -2 );
-
- last if $AuthStat == -1;
- $skip_action{$action}++ if $AuthStat == -2;
+ else {
+ $_ = "RT::Interface::Email::$_" unless /^RT::Interface::Email::/;
+ eval "require $_;";
+ if ($@) {
+ die ("Couldn't load module $_: $@");
+ next;
+ }
+ no strict 'refs';
+ if ( !defined( $Code = *{ $_ . "::GetCurrentUser" }{CODE} ) ) {
+ die ("No GetCurrentUser code found in $_ module");
+ next;
+ }
}
- # strip actions we should skip
- @{$args{Actions}} = grep !$skip_action{$_}, @{$args{Actions}}
- if $AuthStat == -2;
- last unless @{$args{Actions}};
+ ( $CurrentUser, $NewAuthStat ) = $Code->( Message => $Message,
+ CurrentUser => $CurrentUser,
+ AuthLevel => $AuthStat,
+ Action => $args{'action'},
+ Ticket => $SystemTicket,
+ Queue => $SystemQueueObj );
+ # You get the highest level of authentication you were assigned.
last if $AuthStat == -1;
+ $AuthStat = $NewAuthStat if $NewAuthStat > $AuthStat;
}
- return $AuthStat if !wantarray;
-
- return ($AuthStat, $CurrentUser, $error);
-}
-
-sub _RunUnsafeAction {
- my %args = (
- Action => undef,
- ErrorsTo => undef,
- Message => undef,
- Ticket => undef,
- CurrentUser => undef,
- @_
- );
-
- if ( $args{'Action'} =~ /^take$/i ) {
- my ( $status, $msg ) = $args{'Ticket'}->SetOwner( $args{'CurrentUser'}->id );
- unless ($status) {
- MailError(
- To => $args{'ErrorsTo'},
- Subject => "Ticket not taken",
- Explanation => $msg,
- MIMEObj => $args{'Message'}
- );
- return ( 0, "Ticket not taken" );
- }
- } elsif ( $args{'Action'} =~ /^resolve$/i ) {
- my ( $status, $msg ) = $args{'Ticket'}->SetStatus('resolved');
- unless ($status) {
-
- #Warn the sender that we couldn't actually submit the comment.
- MailError(
- To => $args{'ErrorsTo'},
- Subject => "Ticket not resolved",
- Explanation => $msg,
- MIMEObj => $args{'Message'}
- );
- return ( 0, "Ticket not resolved" );
- }
- } else {
- return ( 0, "Not supported unsafe action $args{'Action'}", $args{'Ticket'} );
- }
- return ( 1, "Success" );
-}
-
-=head2 _NoAuthorizedUserFound
-
-Emails the RT Owner and the requestor when the auth plugins return "No auth user found"
-
-=cut
-
-sub _NoAuthorizedUserFound {
- my %args = (
- Right => undef,
- Message => undef,
- Requestor => undef,
- Queue => undef,
- @_
- );
-
- # Notify the RT Admin of the failure.
- MailError(
- To => RT->Config->Get('OwnerEmail'),
- Subject => "Could not load a valid user",
- Explanation => <<EOT,
-RT could not load a valid user, and RT's configuration does not allow
-for the creation of a new user for this email (@{[$args{Requestor}]}).
-
-You might need to grant 'Everyone' the right '@{[$args{Right}]}' for the
-queue @{[$args{'Queue'}]}.
+ # {{{ If authentication fails and no new user was created, get out.
+ if ( !$CurrentUser or !$CurrentUser->Id or $AuthStat == -1 ) {
-EOT
- MIMEObj => $args{'Message'},
- LogLevel => 'error'
- );
-
- # Also notify the requestor that his request has been dropped.
- if ($args{'Requestor'} ne RT->Config->Get('OwnerEmail')) {
- MailError(
- To => $args{'Requestor'},
- Subject => "Could not load a valid user",
- Explanation => <<EOT,
+ # If the plugins refused to create one, they lose.
+ MailError(
+ Subject => "Could not load a valid user",
+ Explanation => <<EOT,
RT could not load a valid user, and RT's configuration does not allow
for the creation of a new user for your email.
+Your RT administrator needs to grant 'Everyone' the right 'CreateTicket'
+for this queue.
+
EOT
- MIMEObj => $args{'Message'},
- LogLevel => 'error'
- );
+ MIMEObj => $Message,
+ LogLevel => 'error' )
+ unless $AuthStat == -1;
+ return ( 0, "Could not load a valid user", undef );
}
-}
-=head2 _HandleMachineGeneratedMail
-
-Takes named params:
- Message
- ErrorsTo
- Subject
-
-Checks the message to see if it's a bounce, if it looks like a loop, if it's autogenerated, etc.
-Returns a triple of ("Should we continue (boolean)", "New value for $ErrorsTo", "Status message",
-"This message appears to be a loop (boolean)" );
-
-=cut
-
-sub _HandleMachineGeneratedMail {
- my %args = ( Message => undef, ErrorsTo => undef, Subject => undef, MessageId => undef, @_ );
- my $head = $args{'Message'}->head;
- my $ErrorsTo = $args{'ErrorsTo'};
-
- my $IsBounce = CheckForBounce($head);
+ # }}}
+ # {{{ Lets check for mail loops of various sorts.
my $IsAutoGenerated = CheckForAutoGenerated($head);
my $IsSuspiciousSender = CheckForSuspiciousSender($head);
@@ -1704,77 +496,153 @@ sub _HandleMachineGeneratedMail {
my $SquelchReplies = 0;
- my $owner_mail = RT->Config->Get('OwnerEmail');
-
#If the message is autogenerated, we need to know, so we can not
# send mail to the sender
- if ( $IsBounce || $IsSuspiciousSender || $IsAutoGenerated || $IsALoop ) {
+ if ( $IsSuspiciousSender || $IsAutoGenerated || $IsALoop ) {
$SquelchReplies = 1;
- $ErrorsTo = $owner_mail;
+ $ErrorsTo = $RT::OwnerEmail;
+ }
+
+ # }}}
+
+ # {{{ Drop it if it's disallowed
+ if ( $AuthStat == 0 ) {
+ MailError(
+ To => $ErrorsTo,
+ Subject => "Permission Denied",
+ Explanation => "You do not have permission to communicate with RT",
+ MIMEObj => $Message );
}
+ # }}}
+ # {{{ Warn someone if it's a loop
+
# Warn someone if it's a loop, before we drop it on the ground
if ($IsALoop) {
- $RT::Logger->crit("RT Received mail (".$args{MessageId}.") from itself.");
+ $RT::Logger->crit("RT Recieved mail ($MessageId) from itself.");
#Should we mail it to RTOwner?
- if ( RT->Config->Get('LoopsToRTOwner') ) {
- MailError(
- To => $owner_mail,
- Subject => "RT Bounce: ".$args{'Subject'},
- Explanation => "RT thinks this message may be a bounce",
- MIMEObj => $args{Message}
- );
- }
+ if ($RT::LoopsToRTOwner) {
+ MailError( To => $RT::OwnerEmail,
+ Subject => "RT Bounce: $Subject",
+ Explanation => "RT thinks this message may be a bounce",
+ MIMEObj => $Message );
- #Do we actually want to store it?
- return ( 0, $ErrorsTo, "Message Bounced", $IsALoop )
- unless RT->Config->Get('StoreLoops');
+ #Do we actually want to store it?
+ return ( 0, "Message Bounced", undef ) unless ($RT::StoreLoops);
+ }
}
- # Squelch replies if necessary
+ # }}}
+
+ # {{{ Squelch replies if necessary
# Don't let the user stuff the RT-Squelch-Replies-To header.
if ( $head->get('RT-Squelch-Replies-To') ) {
- $head->add(
- 'RT-Relocated-Squelch-Replies-To',
- $head->get('RT-Squelch-Replies-To')
- );
+ $head->add( 'RT-Relocated-Squelch-Replies-To',
+ $head->get('RT-Squelch-Replies-To') );
$head->delete('RT-Squelch-Replies-To');
}
if ($SquelchReplies) {
+ ## TODO: This is a hack. It should be some other way to
+ ## indicate that the transaction should be "silent".
- # Squelch replies to the sender, and also leave a clue to
- # allow us to squelch ALL outbound messages. This way we
- # can punt the logic of "what to do when we get a bounce"
- # to the scrip. We might want to notify nobody. Or just
- # the RT Owner. Or maybe all Privileged watchers.
my ( $Sender, $junk ) = ParseSenderAddressFromHead($head);
- $head->add( 'RT-Squelch-Replies-To', $Sender );
- $head->add( 'RT-DetectedAutoGenerated', 'true' );
+ $head->add( 'RT-Squelch-Replies-To', $Sender );
}
- return ( 1, $ErrorsTo, "Handled machine detection", $IsALoop );
-}
-=head2 IsCorrectAction
+ # }}}
+
+ my $Ticket = RT::Ticket->new($CurrentUser);
-Returns a list of valid actions we've found for this message
+ # {{{ If we don't have a ticket Id, we're creating a new ticket
+ if ( !$args{'ticket'} ) {
-=cut
+ # {{{ Create a new ticket
+
+ my @Cc;
+ my @Requestors = ( $CurrentUser->id );
-sub IsCorrectAction {
- my $action = shift;
- my @actions = grep $_, split /-/, $action;
- return ( 0, '(no value)' ) unless @actions;
- foreach ( @actions ) {
- return ( 0, $_ ) unless /^(?:comment|correspond|take|resolve)$/;
+ if ($RT::ParseNewMessageForTicketCcs) {
+ @Cc = ParseCcAddressesFromHead( Head => $head,
+ CurrentUser => $CurrentUser,
+ QueueObj => $SystemQueueObj );
+ }
+
+ my ( $id, $Transaction, $ErrStr ) = $Ticket->Create(
+ Queue => $SystemQueueObj->Id,
+ Subject => $Subject,
+ Requestor => \@Requestors,
+ Cc => \@Cc,
+ MIMEObj => $Message );
+ if ( $id == 0 ) {
+ MailError( To => $ErrorsTo,
+ Subject => "Ticket creation failed",
+ Explanation => $ErrStr,
+ MIMEObj => $Message );
+ $RT::Logger->error("Create failed: $id / $Transaction / $ErrStr ");
+ return ( 0, "Ticket creation failed", $Ticket );
+ }
+
+ # }}}
+ }
+
+ # }}}
+
+ # If the action is comment, add a comment.
+ elsif ( $args{'action'} =~ /^(comment|correspond)$/i ) {
+ $Ticket->Load($args{'ticket'});
+ unless ( $Ticket->Id ) {
+ my $message = "Could not find a ticket with id ".$args{'ticket'};
+ MailError( To => $ErrorsTo,
+ Subject => "Message not recorded",
+ Explanation => $message,
+ MIMEObj => $Message );
+
+ return ( 0, $message);
+ }
+
+ my ( $status, $msg );
+ if ( $args{'action'} =~ /^correspond$/ ) {
+ ( $status, $msg ) = $Ticket->Correspond( MIMEObj => $Message );
+ }
+ else {
+ ( $status, $msg ) = $Ticket->Comment( MIMEObj => $Message );
+ }
+ unless ($status) {
+
+ #Warn the sender that we couldn't actually submit the comment.
+ MailError( To => $ErrorsTo,
+ Subject => "Message not recorded",
+ Explanation => $msg,
+ MIMEObj => $Message );
+ return ( 0, "Message not recorded", $Ticket );
+ }
}
- return ( 1, @actions );
+
+ else {
+
+ #Return mail to the sender with an error
+ MailError( To => $ErrorsTo,
+ Subject => "RT Configuration error",
+ Explanation => "'"
+ . $args{'action'}
+ . "' not a recognized action."
+ . " Your RT administrator has misconfigured "
+ . "the mail aliases which invoke RT",
+ MIMEObj => $Message );
+ $RT::Logger->crit( $args{'action'} . " type unknown for $MessageId" );
+ return ( 0, "Configuration error: " . $args{'action'} . " not a recognized action", $Ticket );
+
+ }
+
+
+return ( 1, "Success", $Ticket );
}
eval "require RT::Interface::Email_Vendor";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Interface/Email_Vendor.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email_Vendor.pm});
eval "require RT::Interface::Email_Local";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Interface/Email_Local.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Email_Local.pm});
1;
diff --git a/rt/lib/RT/Interface/Web.pm b/rt/lib/RT/Interface/Web.pm
index b4279fb4b..5097f54a4 100644
--- a/rt/lib/RT/Interface/Web.pm
+++ b/rt/lib/RT/Interface/Web.pm
@@ -1,734 +1,148 @@
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@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.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+#
+# 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
## Portions Copyright 2000 Tobias Brox <tobix@fsck.com>
## This is a library of static subs to be used by the Mason web
## interface to RT
+
=head1 NAME
RT::Interface::Web
+=begin testing
-=cut
-
-use strict;
-use warnings;
-
-package RT::Interface::Web;
-
-use RT::SavedSearches;
-use URI qw();
-use RT::Interface::Web::Session;
-use Digest::MD5 ();
-use Encode qw();
-
-# {{{ EscapeUTF8
-
-=head2 EscapeUTF8 SCALARREF
+use_ok(RT::Interface::Web);
-does a css-busting but minimalist escaping of whatever html you're passing in.
+=end testing
=cut
-sub EscapeUTF8 {
- my $ref = shift;
- return unless defined $$ref;
-
- $$ref =~ s/&/&#38;/g;
- $$ref =~ s/</&lt;/g;
- $$ref =~ s/>/&gt;/g;
- $$ref =~ s/\(/&#40;/g;
- $$ref =~ s/\)/&#41;/g;
- $$ref =~ s/"/&#34;/g;
- $$ref =~ s/'/&#39;/g;
-}
-
-# }}}
-
-# {{{ EscapeURI
-
-=head2 EscapeURI SCALARREF
-Escapes URI component according to RFC2396
-
-=cut
-
-sub EscapeURI {
- my $ref = shift;
- return unless defined $$ref;
-
- use bytes;
- $$ref =~ s/([^a-zA-Z0-9_.!~*'()-])/uc sprintf("%%%02X", ord($1))/eg;
-}
-
-# }}}
+package RT::Interface::Web;
+use strict;
-# {{{ WebCanonicalizeInfo
-=head2 WebCanonicalizeInfo();
-Different web servers set different environmental varibles. This
-function must return something suitable for REMOTE_USER. By default,
-just downcase $ENV{'REMOTE_USER'}
-=cut
-sub WebCanonicalizeInfo {
- return $ENV{'REMOTE_USER'} ? lc $ENV{'REMOTE_USER'} : $ENV{'REMOTE_USER'};
-}
+# {{{ sub NewApacheHandler
-# }}}
+=head2 NewApacheHandler
-# {{{ WebExternalAutoInfo
-
-=head2 WebExternalAutoInfo($user);
-
-Returns a hash of user attributes, used when WebExternalAuto is set.
+ Takes extra options to pass to HTML::Mason::ApacheHandler->new
+ Returns a new Mason::ApacheHandler object
=cut
-sub WebExternalAutoInfo {
- my $user = shift;
-
- my %user_info;
-
- # default to making Privileged users, even if they specify
- # some other default Attributes
- if ( !$RT::AutoCreate
- || ( ref($RT::AutoCreate) && not exists $RT::AutoCreate->{Privileged} ) )
- {
- $user_info{'Privileged'} = 1;
- }
-
- if ( $^O !~ /^(?:riscos|MacOS|MSWin32|dos|os2)$/ ) {
-
- # Populate fields with information from Unix /etc/passwd
-
- my ( $comments, $realname ) = ( getpwnam($user) )[ 5, 6 ];
- $user_info{'Comments'} = $comments if defined $comments;
- $user_info{'RealName'} = $realname if defined $realname;
- } elsif ( $^O eq 'MSWin32' and eval 'use Net::AdminMisc; 1' ) {
-
- # Populate fields with information from NT domain controller
- }
+sub NewApacheHandler {
+ require HTML::Mason::ApacheHandler;
+ my $ah = new HTML::Mason::ApacheHandler(
+
+ comp_root => [
+ [ local => $RT::MasonLocalComponentRoot ],
+ [ standard => $RT::MasonComponentRoot ]
+ ],
+ args_method => "CGI",
+ default_escape_flags => 'h',
+ allow_globals => [qw(%session)],
+ data_dir => "$RT::MasonDataDir",
+ @_
+ );
- # and return the wad of stuff
- return {%user_info};
+ $ah->interp->set_escape( h => \&RT::Interface::Web::EscapeUTF8 );
+
+ return ($ah);
}
# }}}
-sub HandleRequest {
- my $ARGS = shift;
-
- $HTML::Mason::Commands::r->content_type("text/html; charset=utf-8");
-
- $HTML::Mason::Commands::m->{'rt_base_time'} = [ Time::HiRes::gettimeofday() ];
-
- # Roll back any dangling transactions from a previous failed connection
- $RT::Handle->ForceRollback() if $RT::Handle->TransactionDepth;
-
- MaybeEnableSQLStatementLog();
-
- # avoid reentrancy, as suggested by masonbook
- local *HTML::Mason::Commands::session unless $HTML::Mason::Commands::m->is_subrequest;
-
- $HTML::Mason::Commands::m->autoflush( $HTML::Mason::Commands::m->request_comp->attr('AutoFlush') )
- if ( $HTML::Mason::Commands::m->request_comp->attr_exists('AutoFlush') );
-
- DecodeARGS($ARGS);
- PreprocessTimeUpdates($ARGS);
-
- MaybeShowInstallModePage();
-
- $HTML::Mason::Commands::m->comp( '/Elements/SetupSessionCookie', %$ARGS );
- SendSessionCookie();
- $HTML::Mason::Commands::session{'CurrentUser'} = RT::CurrentUser->new() unless _UserLoggedIn();
-
- MaybeShowNoAuthPage($ARGS);
-
- AttemptExternalAuth($ARGS) if RT->Config->Get('WebExternalAuthContinuous') or not _UserLoggedIn();
-
- _ForceLogout() unless _UserLoggedIn();
-
- # Process per-page authentication callbacks
- $HTML::Mason::Commands::m->callback( %$ARGS, CallbackName => 'Auth', CallbackPage => '/autohandler' );
-
- unless ( _UserLoggedIn() ) {
- _ForceLogout();
-
- # If the user is logging in, let's authenticate
- if ( defined $ARGS->{user} && defined $ARGS->{pass} ) {
- AttemptPasswordAuthentication($ARGS);
- } else {
- # if no credentials then show him login page
- $HTML::Mason::Commands::m->comp( '/Elements/Login', %$ARGS );
- $HTML::Mason::Commands::m->abort;
- }
- }
-
- # now it applies not only to home page, but any dashboard that can be used as a workspace
- $HTML::Mason::Commands::session{'home_refresh_interval'} = $ARGS->{'HomeRefreshInterval'}
- if ( $ARGS->{'HomeRefreshInterval'} );
-
- # Process per-page global callbacks
- $HTML::Mason::Commands::m->callback( %$ARGS, CallbackName => 'Default', CallbackPage => '/autohandler' );
-
- ShowRequestedPage($ARGS);
- LogRecordedSQLStatements();
-}
-
-sub _ForceLogout {
-
- delete $HTML::Mason::Commands::session{'CurrentUser'};
-}
-
-sub _UserLoggedIn {
- if ( $HTML::Mason::Commands::session{CurrentUser} && $HTML::Mason::Commands::session{'CurrentUser'}->id ) {
- return 1;
- } else {
- return undef;
- }
-
-}
-
-=head2 MaybeShowInstallModePage
-
-This function, called exclusively by RT's autohandler, dispatches
-a request to RT's Installation workflow, only if Install Mode is enabled in the configuration file.
-
-If it serves a page, it stops mason processing. Otherwise, mason just keeps running through the autohandler
-
-=cut
-
-sub MaybeShowInstallModePage {
- return unless RT->InstallMode;
-
- my $m = $HTML::Mason::Commands::m;
- if ( $m->base_comp->path =~ RT->Config->Get('WebNoAuthRegex') ) {
- $m->call_next();
- } elsif ( $m->request_comp->path !~ '^(/+)Install/' ) {
- RT::Interface::Web::Redirect( RT->Config->Get('WebURL') . "Install/index.html" );
- } else {
- $m->call_next();
- }
- $m->abort();
-}
-
-=head2 MaybeShowNoAuthPage \%ARGS
-
-This function, called exclusively by RT's autohandler, dispatches
-a request to the page a user requested (but only if it matches the "noauth" regex.
-
-If it serves a page, it stops mason processing. Otherwise, mason just keeps running through the autohandler
-
-=cut
-
-sub MaybeShowNoAuthPage {
- my $ARGS = shift;
-
- my $m = $HTML::Mason::Commands::m;
-
- return unless $m->base_comp->path =~ RT->Config->Get('WebNoAuthRegex');
-
- # If it's a noauth file, don't ask for auth.
- SendSessionCookie();
- $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %$ARGS );
- $m->abort;
-}
-
-=head2 ShowRequestedPage \%ARGS
-
-This function, called exclusively by RT's autohandler, dispatches
-a request to the page a user requested (making sure that unpriviled users
-can only see self-service pages.
-
-=cut
-
-sub ShowRequestedPage {
- my $ARGS = shift;
-
- my $m = $HTML::Mason::Commands::m;
-
- SendSessionCookie();
-
- # If the user isn't privileged, they can only see SelfService
- unless ( $HTML::Mason::Commands::session{'CurrentUser'}->Privileged ) {
-
- # if the user is trying to access a ticket, redirect them
- if ( $m->request_comp->path =~ '^(/+)Ticket/Display.html' && $ARGS->{'id'} ) {
- RT::Interface::Web::Redirect( RT->Config->Get('WebURL') . "SelfService/Display.html?id=" . $ARGS->{'id'} );
- }
-
- # otherwise, drop the user at the SelfService default page
- elsif ( $m->base_comp->path !~ RT->Config->Get('SelfServiceRegex') ) {
- RT::Interface::Web::Redirect( RT->Config->Get('WebURL') . "SelfService/" );
- }
-
- # if user is in SelfService dir let him do anything
- else {
- $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %$ARGS );
- }
- } else {
- $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %$ARGS );
- }
-
-}
-
-sub AttemptExternalAuth {
- my $ARGS = shift;
-
- return unless ( RT->Config->Get('WebExternalAuth') );
-
- my $user = $ARGS->{user};
- my $m = $HTML::Mason::Commands::m;
-
- # If RT is configured for external auth, let's go through and get REMOTE_USER
-
- # do we actually have a REMOTE_USER equivlent?
- if ( RT::Interface::Web::WebCanonicalizeInfo() ) {
- my $orig_user = $user;
-
- $user = RT::Interface::Web::WebCanonicalizeInfo();
- my $load_method = RT->Config->Get('WebExternalGecos') ? 'LoadByGecos' : 'Load';
-
- if ( $^O eq 'MSWin32' and RT->Config->Get('WebExternalGecos') ) {
- my $NodeName = Win32::NodeName();
- $user =~ s/^\Q$NodeName\E\\//i;
- }
-
- InstantiateNewSession() unless _UserLoggedIn;
- $HTML::Mason::Commands::session{'CurrentUser'} = RT::CurrentUser->new();
- $HTML::Mason::Commands::session{'CurrentUser'}->$load_method($user);
-
- if ( RT->Config->Get('WebExternalAuto') and not _UserLoggedIn() ) {
-
- # Create users on-the-fly
- my $UserObj = RT::User->new($RT::SystemUser);
- my ( $val, $msg ) = $UserObj->Create(
- %{ ref RT->Config->Get('AutoCreate') ? RT->Config->Get('AutoCreate') : {} },
- Name => $user,
- Gecos => $user,
- );
-
- if ($val) {
-
- # now get user specific information, to better create our user.
- my $new_user_info = RT::Interface::Web::WebExternalAutoInfo($user);
-
- # set the attributes that have been defined.
- foreach my $attribute ( $UserObj->WritableAttributes ) {
- $m->callback(
- Attribute => $attribute,
- User => $user,
- UserInfo => $new_user_info,
- CallbackName => 'NewUser',
- CallbackPage => '/autohandler'
- );
- my $method = "Set$attribute";
- $UserObj->$method( $new_user_info->{$attribute} ) if defined $new_user_info->{$attribute};
- }
- $HTML::Mason::Commands::session{'CurrentUser'}->Load($user);
- } else {
-
- # we failed to successfully create the user. abort abort abort.
- delete $HTML::Mason::Commands::session{'CurrentUser'};
- $m->comp( '/Elements/Login', %$ARGS, Error => HTML::Mason::Commands::loc( 'Cannot create user: [_1]', $msg ) )
- if RT->Config->Get('WebFallbackToInternalAuth');;
- $m->abort();
- }
- }
-
- if ( _UserLoggedIn() ) {
- $m->callback( %$ARGS, CallbackName => 'ExternalAuthSuccessfulLogin', CallbackPage => '/autohandler' );
- } else {
- delete $HTML::Mason::Commands::session{'CurrentUser'};
- $user = $orig_user;
-
- if ( RT->Config->Get('WebExternalOnly') ) {
- $m->comp( '/Elements/Login', %$ARGS, Error => HTML::Mason::Commands::loc('You are not an authorized user') );
- $m->abort();
- }
- }
- } elsif ( RT->Config->Get('WebFallbackToInternalAuth') ) {
- unless ( defined $HTML::Mason::Commands::session{'CurrentUser'} ) {
- # XXX unreachable due to prior defaulting in HandleRequest (check c34d108)
- $m->comp( '/Elements/Login', %$ARGS, Error => HTML::Mason::Commands::loc('You are not an authorized user') );
- $m->abort();
- }
- } else {
-
- # WebExternalAuth is set, but we don't have a REMOTE_USER. abort
- # XXX: we must return AUTH_REQUIRED status or we fallback to
- # internal auth here too.
- delete $HTML::Mason::Commands::session{'CurrentUser'}
- if defined $HTML::Mason::Commands::session{'CurrentUser'};
- }
-}
+# {{{ sub NewCGIHandler
-sub AttemptPasswordAuthentication {
- my $ARGS = shift;
- my $user_obj = RT::CurrentUser->new();
- $user_obj->Load( $ARGS->{user} );
+=head2 NewCGIHandler
- my $m = $HTML::Mason::Commands::m;
-
- unless ( $user_obj->id && $user_obj->IsPassword( $ARGS->{pass} ) ) {
- $RT::Logger->error("FAILED LOGIN for @{[$ARGS->{user}]} from $ENV{'REMOTE_ADDR'}");
- $m->comp( '/Elements/Login', %$ARGS, Error => HTML::Mason::Commands::loc('Your username or password is incorrect'), );
- $m->callback( %$ARGS, CallbackName => 'FailedLogin', CallbackPage => '/autohandler' );
- $m->abort;
- }
-
- $RT::Logger->info("Successful login for @{[$ARGS->{user}]} from $ENV{'REMOTE_ADDR'}");
- InstantiateNewSession();
- $HTML::Mason::Commands::session{'CurrentUser'} = $user_obj;
- $m->callback( %$ARGS, CallbackName => 'SuccessfulLogin', CallbackPage => '/autohandler' );
-}
-
-=head2 LoadSessionFromCookie
-
-Load or setup a session cookie for the current user.
+ Returns a new Mason::CGIHandler object
=cut
-sub _SessionCookieName {
- my $cookiename = "RT_SID_" . RT->Config->Get('rtname');
- $cookiename .= "." . $ENV{'SERVER_PORT'} if $ENV{'SERVER_PORT'};
- return $cookiename;
-}
-
-sub LoadSessionFromCookie {
-
- my %cookies = CGI::Cookie->fetch;
- my $cookiename = _SessionCookieName();
- my $SessionCookie = ( $cookies{$cookiename} ? $cookies{$cookiename}->value : undef );
- tie %HTML::Mason::Commands::session, 'RT::Interface::Web::Session', $SessionCookie;
- unless ( $SessionCookie && $HTML::Mason::Commands::session{'_session_id'} eq $SessionCookie ) {
- undef $cookies{$cookiename};
- }
- if ( int RT->Config->Get('AutoLogoff') ) {
- my $now = int( time / 60 );
- my $last_update = $HTML::Mason::Commands::session{'_session_last_update'} || 0;
-
- if ( $last_update && ( $now - $last_update - RT->Config->Get('AutoLogoff') ) > 0 ) {
- InstantiateNewSession();
- }
-
- # save session on each request when AutoLogoff is turned on
- $HTML::Mason::Commands::session{'_session_last_update'} = $now if $now != $last_update;
- }
-}
-
-sub InstantiateNewSession {
- tied(%HTML::Mason::Commands::session)->delete if tied(%HTML::Mason::Commands::session);
- tie %HTML::Mason::Commands::session, 'RT::Interface::Web::Session', undef;
-}
-
-sub SendSessionCookie {
- my $cookie = CGI::Cookie->new(
- -name => _SessionCookieName(),
- -value => $HTML::Mason::Commands::session{_session_id},
- -path => RT->Config->Get('WebPath'),
- -secure => ( RT->Config->Get('WebSecureCookies') ? 1 : 0 )
+sub NewCGIHandler {
+ my %args = (
+ @_
);
- $HTML::Mason::Commands::r->err_headers_out->{'Set-Cookie'} = $cookie->as_string;
-}
-
-=head2 Redirect URL
-
-This routine ells the current user's browser to redirect to URL.
-Additionally, it unties the user's currently active session, helping to avoid
-A bug in Apache::Session 1.81 and earlier which clobbers sessions if we try to use
-a cached DBI statement handle twice at the same time.
-
-=cut
-
-sub Redirect {
- my $redir_to = shift;
- untie $HTML::Mason::Commands::session;
- my $uri = URI->new($redir_to);
- my $server_uri = URI->new( RT->Config->Get('WebURL') );
-
- # If the user is coming in via a non-canonical
- # hostname, don't redirect them to the canonical host,
- # it will just upset them (and invalidate their credentials)
- # don't do this if $RT::CanoniaclRedirectURLs is true
- if ( !RT->Config->Get('CanonicalizeRedirectURLs')
- && $uri->host eq $server_uri->host
- && $uri->port eq $server_uri->port )
- {
- if ( defined $ENV{HTTPS} and $ENV{'HTTPS'} eq 'on' ) {
- $uri->scheme('https');
- } else {
- $uri->scheme('http');
- }
-
- # [rt3.fsck.com #12716] Apache recommends use of $SERVER_HOST
- $uri->host( $ENV{'SERVER_HOST'} || $ENV{'HTTP_HOST'} );
- $uri->port( $ENV{'SERVER_PORT'} );
- }
-
- # not sure why, but on some systems without this call mason doesn't
- # set status to 302, but 200 instead and people see blank pages
- $HTML::Mason::Commands::r->status(302);
-
- # Perlbal expects a status message, but Mason's default redirect status
- # doesn't provide one. See also rt.cpan.org #36689.
- $HTML::Mason::Commands::m->redirect( $uri->canonical, "302 Found" );
-
- $HTML::Mason::Commands::m->abort;
-}
-
-=head2 StaticFileHeaders
-
-Send the browser a few headers to try to get it to (somewhat agressively)
-cache RT's static Javascript and CSS files.
-
-This routine could really use _accurate_ heuristics. (XXX TODO)
-
-=cut
+ my $handler = HTML::Mason::CGIHandler->new(
+ comp_root => [
+ [ local => $RT::MasonLocalComponentRoot ],
+ [ standard => $RT::MasonComponentRoot ]
+ ],
+ data_dir => "$RT::MasonDataDir",
+ default_escape_flags => 'h',
+ allow_globals => [qw(%session)]
+ );
+
-sub StaticFileHeaders {
- my $date = RT::Date->new($RT::SystemUser);
+ $handler->interp->set_escape( h => \&RT::Interface::Web::EscapeUTF8 );
- # make cache public
- $HTML::Mason::Commands::r->headers_out->{'Cache-Control'} = 'max-age=259200, public';
- # Expire things in a month.
- $date->Set( Value => time + 30 * 24 * 60 * 60 );
- $HTML::Mason::Commands::r->headers_out->{'Expires'} = $date->RFC2616;
+ return ($handler);
- # if we set 'Last-Modified' then browser request a comp using 'If-Modified-Since'
- # request, but we don't handle it and generate full reply again
- # Last modified at server start time
- # $date->Set( Value => $^T );
- # $HTML::Mason::Commands::r->headers_out->{'Last-Modified'} = $date->RFC2616;
}
+# }}}
-=head2 SendStaticFile
-Takes a File => path and a Type => Content-type
+# {{{ EscapeUTF8
-If Type isn't provided and File is an image, it will
-figure out a sane Content-type, otherwise it will
-send application/octet-stream
+=head2 EscapeUTF8 SCALARREF
-Will set caching headers using StaticFileHeaders
+does a css-busting but minimalist escaping of whatever html you're passing in.
=cut
-sub SendStaticFile {
- my $self = shift;
- my %args = @_;
- my $file = $args{File};
- my $type = $args{Type};
-
- $self->StaticFileHeaders();
-
- unless ($type) {
- if ( $file =~ /\.(gif|png|jpe?g)$/i ) {
- $type = "image/$1";
- $type =~ s/jpg/jpeg/gi;
- }
- $type ||= "application/octet-stream";
- }
- $HTML::Mason::Commands::r->content_type($type);
- open my $fh, "<$file" or die "couldn't open file: $!";
- binmode($fh);
- {
- local $/ = \16384;
- $HTML::Mason::Commands::m->out($_) while (<$fh>);
- $HTML::Mason::Commands::m->flush_buffer;
- }
- close $fh;
-}
-
-sub StripContent {
- my %args = @_;
- my $content = $args{Content};
- return '' unless $content;
-
- # Make the content have no 'weird' newlines in it
- $content =~ s/\r+\n/\n/g;
-
- my $return_content = $content;
-
- my $html = $args{ContentType} && $args{ContentType} eq "text/html";
- my $sigonly = $args{StripSignature};
-
- # massage content to easily detect if there's any real content
- $content =~ s/\s+//g; # yes! remove all the spaces
- if ( $html ) {
- # remove html version of spaces and newlines
- $content =~ s!&nbsp;!!g;
- $content =~ s!<br/?>!!g;
- }
-
- # Filter empty content when type is text/html
- return '' if $html && $content !~ /\S/;
-
- # If we aren't supposed to strip the sig, just bail now.
- return $return_content unless $sigonly;
-
- # Find the signature
- my $sig = $args{'CurrentUser'}->UserObj->Signature || '';
- $sig =~ s/\s+//g;
-
- # Check for plaintext sig
- return '' if not $html and $content =~ /^(--)?\Q$sig\E$/;
-
- # Check for html-formatted sig
- RT::Interface::Web::EscapeUTF8( \$sig );
- return ''
- if $html
- and $content =~ m{^(?:<p>)?(--)?\Q$sig\E(?:</p>)?$}s;
-
- # Pass it through
- return $return_content;
-}
-
-sub DecodeARGS {
- my $ARGS = shift;
-
- %{$ARGS} = map {
-
- # if they've passed multiple values, they'll be an array. if they've
- # passed just one, a scalar whatever they are, mark them as utf8
- my $type = ref($_);
- ( !$type )
- ? Encode::is_utf8($_)
- ? $_
- : Encode::decode( 'UTF-8' => $_, Encode::FB_PERLQQ )
- : ( $type eq 'ARRAY' )
- ? [ map { ( ref($_) or Encode::is_utf8($_) ) ? $_ : Encode::decode( 'UTF-8' => $_, Encode::FB_PERLQQ ) }
- @$_ ]
- : ( $type eq 'HASH' )
- ? { map { ( ref($_) or Encode::is_utf8($_) ) ? $_ : Encode::decode( 'UTF-8' => $_, Encode::FB_PERLQQ ) }
- %$_ }
- : $_
- } %$ARGS;
-}
-
-sub PreprocessTimeUpdates {
- my $ARGS = shift;
-
- # Later in the code we use
- # $m->comp( { base_comp => $m->request_comp }, $m->fetch_next, %ARGS );
- # instead of $m->call_next to avoid problems with UTF8 keys in arguments.
- # The call_next method pass through original arguments and if you have
- # an argument with unicode key then in a next component you'll get two
- # records in the args hash: one with key without UTF8 flag and another
- # with the flag, which may result into errors. "{ base_comp => $m->request_comp }"
- # is copied from mason's source to get the same results as we get from
- # call_next method, this feature is not documented, so we just leave it
- # here to avoid possible side effects.
-
- # This code canonicalizes time inputs in hours into minutes
- foreach my $field ( keys %$ARGS ) {
- next unless $field =~ /^(.*)-TimeUnits$/i && $ARGS->{$1};
- my $local = $1;
- $ARGS->{$local} =~ s{\b (?: (\d+) \s+ )? (\d+)/(\d+) \b}
- {($1 || 0) + $3 ? $2 / $3 : 0}xe;
- if ( $ARGS->{$field} && $ARGS->{$field} =~ /hours/i ) {
- $ARGS->{$local} *= 60;
- }
- delete $ARGS->{$field};
- }
-
-}
-
-sub MaybeEnableSQLStatementLog {
-
- my $log_sql_statements = RT->Config->Get('StatementLog');
-
- if ($log_sql_statements) {
- $RT::Handle->ClearSQLStatementLog;
- $RT::Handle->LogSQLStatements(1);
- }
+sub EscapeUTF8 {
+ my $ref = shift;
+ my $val = $$ref;
+ use bytes;
+ $val =~ s/&/&#38;/g;
+ $val =~ s/</&lt;/g;
+ $val =~ s/>/&gt;/g;
+ $val =~ s/\(/&#40;/g;
+ $val =~ s/\)/&#41;/g;
+ $val =~ s/"/&#34;/g;
+ $val =~ s/'/&#39;/g;
+ $$ref = $val;
+ Encode::_utf8_on($$ref);
}
-sub LogRecordedSQLStatements {
- my $log_sql_statements = RT->Config->Get('StatementLog');
-
- return unless ($log_sql_statements);
-
- my @log = $RT::Handle->SQLStatementLog;
- $RT::Handle->ClearSQLStatementLog;
- for my $stmt (@log) {
- my ( $time, $sql, $bind, $duration ) = @{$stmt};
- my @bind;
- if ( ref $bind ) {
- @bind = @{$bind};
- } else {
-
- # Older DBIx-SB
- $duration = $bind;
- }
- $RT::Logger->log(
- level => $log_sql_statements,
- message => "SQL("
- . sprintf( "%.6f", $duration )
- . "s): $sql;"
- . ( @bind ? " [ bound values: @{[map{qq|'$_'|} @bind]} ]" : "" )
- );
- }
+# }}}
-}
package HTML::Mason::Commands;
-
+use strict;
use vars qw/$r $m %session/;
+
# {{{ loc
=head2 loc ARRAY
@@ -742,26 +156,19 @@ through
sub loc {
- if ( $session{'CurrentUser'}
- && UNIVERSAL::can( $session{'CurrentUser'}, 'loc' ) )
- {
- return ( $session{'CurrentUser'}->loc(@_) );
- } elsif (
- my $u = eval {
- RT::CurrentUser->new();
- }
- )
- {
- return ( $u->loc(@_) );
- } else {
-
- # pathetic case -- SystemUser is gone.
- return $_[0];
+ if ($session{'CurrentUser'} &&
+ UNIVERSAL::can($session{'CurrentUser'}, 'loc')){
+ return($session{'CurrentUser'}->loc(@_));
+ }
+ else {
+ my $u = RT::CurrentUser->new($RT::SystemUser);
+ return ($u->loc(@_));
}
}
# }}}
+
# {{{ loc_fuzzy
=head2 loc_fuzzy STRING
@@ -775,41 +182,40 @@ inside the lexicon file.
=cut
sub loc_fuzzy {
- my $msg = shift;
-
- if ( $session{'CurrentUser'}
- && UNIVERSAL::can( $session{'CurrentUser'}, 'loc' ) )
- {
- return ( $session{'CurrentUser'}->loc_fuzzy($msg) );
- } else {
- my $u = RT::CurrentUser->new( $RT::SystemUser->Id );
- return ( $u->loc_fuzzy($msg) );
+ my $msg = shift;
+
+ if ($session{'CurrentUser'} &&
+ UNIVERSAL::can($session{'CurrentUser'}, 'loc')){
+ return($session{'CurrentUser'}->loc_fuzzy($msg));
+ }
+ else {
+ my $u = RT::CurrentUser->new($RT::SystemUser);
+ return ($u->loc_fuzzy($msg));
}
}
# }}}
+
# {{{ sub Abort
# Error - calls Error and aborts
sub Abort {
- my $why = shift;
- my %args = @_;
- if ( $session{'ErrorDocument'}
- && $session{'ErrorDocumentType'} )
- {
- $r->content_type( $session{'ErrorDocumentType'} );
- $m->comp( $session{'ErrorDocument'}, Why => $why, %args );
+ if ($session{'ErrorDocument'} &&
+ $session{'ErrorDocumentType'}) {
+ $r->content_type($session{'ErrorDocumentType'});
+ $m->comp($session{'ErrorDocument'} , Why => shift);
$m->abort;
- } else {
- $m->comp( "/Elements/Error", Why => $why, %args );
+ }
+ else {
+ $m->comp("/Elements/Error" , Why => shift);
$m->abort;
}
}
# }}}
-# {{{ sub CreateTicket
+# {{{ sub CreateTicket
=head2 CreateTicket ARGS
@@ -833,160 +239,80 @@ sub CreateTicket {
Abort('You have no permission to create tickets in that queue.');
}
- my $due;
- if ( defined $ARGS{'Due'} and $ARGS{'Due'} =~ /\S/ ) {
- $due = new RT::Date( $session{'CurrentUser'} );
- $due->Set( Format => 'unknown', Value => $ARGS{'Due'} );
- }
- my $starts;
- if ( defined $ARGS{'Starts'} and $ARGS{'Starts'} =~ /\S/ ) {
- $starts = new RT::Date( $session{'CurrentUser'} );
- $starts->Set( Format => 'unknown', Value => $ARGS{'Starts'} );
- }
+ my $due = new RT::Date( $session{'CurrentUser'} );
+ $due->Set( Format => 'unknown', Value => $ARGS{'Due'} );
+ my $starts = new RT::Date( $session{'CurrentUser'} );
+ $starts->Set( Format => 'unknown', Value => $ARGS{'Starts'} );
- my $sigless = RT::Interface::Web::StripContent(
- Content => $ARGS{Content},
- ContentType => $ARGS{ContentType},
- StripSignature => 1,
- CurrentUser => $session{'CurrentUser'},
- );
+ my @Requestors = split ( /\s*,\s*/, $ARGS{'Requestors'} );
+ my @Cc = split ( /\s*,\s*/, $ARGS{'Cc'} );
+ my @AdminCc = split ( /\s*,\s*/, $ARGS{'AdminCc'} );
my $MIMEObj = MakeMIMEEntity(
- Subject => $ARGS{'Subject'},
- From => $ARGS{'From'},
- Cc => $ARGS{'Cc'},
- Body => $sigless,
- Type => $ARGS{'ContentType'},
+ Subject => $ARGS{'Subject'},
+ From => $ARGS{'From'},
+ Cc => $ARGS{'Cc'},
+ Body => $ARGS{'Content'},
);
- if ( $ARGS{'Attachments'} ) {
- my $rv = $MIMEObj->make_multipart;
- $RT::Logger->error("Couldn't make multipart message")
- if !$rv || $rv !~ /^(?:DONE|ALREADY)$/;
-
- foreach ( values %{ $ARGS{'Attachments'} } ) {
- unless ($_) {
- $RT::Logger->error("Couldn't add empty attachemnt");
- next;
- }
- $MIMEObj->add_part($_);
- }
- }
-
- foreach my $argument (qw(Encrypt Sign)) {
- $MIMEObj->head->add( "X-RT-$argument" => $ARGS{$argument} ) if defined $ARGS{$argument};
+ if ($ARGS{'Attachments'}) {
+ $MIMEObj->make_multipart;
+ $MIMEObj->add_part($_) foreach values %{$ARGS{'Attachments'}};
}
my %create_args = (
- Type => $ARGS{'Type'} || 'ticket',
- Queue => $ARGS{'Queue'},
- Owner => $ARGS{'Owner'},
-
- # note: name change
- Requestor => $ARGS{'Requestors'},
- Cc => $ARGS{'Cc'},
- AdminCc => $ARGS{'AdminCc'},
+ Queue => $ARGS{'Queue'},
+ Owner => $ARGS{'Owner'},
InitialPriority => $ARGS{'InitialPriority'},
FinalPriority => $ARGS{'FinalPriority'},
TimeLeft => $ARGS{'TimeLeft'},
- TimeEstimated => $ARGS{'TimeEstimated'},
+ TimeEstimated => $ARGS{'TimeEstimated'},
TimeWorked => $ARGS{'TimeWorked'},
+ Requestor => \@Requestors,
+ Cc => \@Cc,
+ AdminCc => \@AdminCc,
Subject => $ARGS{'Subject'},
Status => $ARGS{'Status'},
- Due => $due ? $due->ISO : undef,
- Starts => $starts ? $starts->ISO : undef,
+ Due => $due->ISO,
+ Starts => $starts->ISO,
MIMEObj => $MIMEObj
);
-
- my @temp_squelch;
- foreach my $type (qw(Requestor Cc AdminCc)) {
- push @temp_squelch, map $_->address, Email::Address->parse( $create_args{$type} )
- if grep $_ eq $type || $_ eq ( $type . 's' ), @{ $ARGS{'SkipNotification'} || [] };
-
- }
-
- if (@temp_squelch) {
- require RT::Action::SendEmail;
- RT::Action::SendEmail->SquelchMailTo( RT::Action::SendEmail->SquelchMailTo, @temp_squelch );
+ foreach my $arg (%ARGS) {
+ if ($arg =~ /^CustomField-(\d+)(.*?)$/) {
+ next if ($arg =~ /-Magic$/);
+ $create_args{"CustomField-".$1} = $ARGS{"$arg"};
+ }
}
-
- if ( $ARGS{'AttachTickets'} ) {
- require RT::Action::SendEmail;
- RT::Action::SendEmail->AttachTickets( RT::Action::SendEmail->AttachTickets,
- ref $ARGS{'AttachTickets'}
- ? @{ $ARGS{'AttachTickets'} }
- : ( $ARGS{'AttachTickets'} ) );
+ my ( $id, $Trans, $ErrMsg ) = $Ticket->Create(%create_args);
+ unless ( $id && $Trans ) {
+ Abort($ErrMsg);
}
+ my @linktypes = qw( DependsOn MemberOf RefersTo );
- foreach my $arg ( keys %ARGS ) {
- next if $arg =~ /-(?:Magic|Category)$/;
-
- if ( $arg =~ /^Object-RT::Transaction--CustomField-/ ) {
- $create_args{$arg} = $ARGS{$arg};
+ foreach my $linktype (@linktypes) {
+ foreach my $luri ( split ( / /, $ARGS{"new-$linktype"} ) ) {
+ $luri =~ s/\s*$//; # Strip trailing whitespace
+ my ( $val, $msg ) = $Ticket->AddLink(
+ Target => $luri,
+ Type => $linktype
+ );
+ push ( @Actions, $msg ) unless ($val);
}
- # Object-RT::Ticket--CustomField-3-Values
- elsif ( $arg =~ /^Object-RT::Ticket--CustomField-(\d+)/ ) {
- my $cfid = $1;
-
- my $cf = RT::CustomField->new( $session{'CurrentUser'} );
- $cf->Load($cfid);
- unless ( $cf->id ) {
- $RT::Logger->error( "Couldn't load custom field #" . $cfid );
- next;
- }
-
- if ( $arg =~ /-Upload$/ ) {
- $create_args{"CustomField-$cfid"} = _UploadedFile($arg);
- next;
- }
-
- my $type = $cf->Type;
-
- my @values = ();
- if ( ref $ARGS{$arg} eq 'ARRAY' ) {
- @values = @{ $ARGS{$arg} };
- } elsif ( $type =~ /text/i ) {
- @values = ( $ARGS{$arg} );
- } else {
- no warnings 'uninitialized';
- @values = split /\r*\n/, $ARGS{$arg};
- }
- @values = grep length, map {
- s/\r+\n/\n/g;
- s/^\s+//;
- s/\s+$//;
- $_;
- }
- grep defined, @values;
+ foreach my $luri ( split ( / /, $ARGS{"$linktype-new"} ) ) {
+ my ( $val, $msg ) = $Ticket->AddLink(
+ Base => $luri,
+ Type => $linktype
+ );
- $create_args{"CustomField-$cfid"} = \@values;
+ push ( @Actions, $msg ) unless ($val);
}
}
- # turn new link lists into arrays, and pass in the proper arguments
- my %map = (
- 'new-DependsOn' => 'DependsOn',
- 'DependsOn-new' => 'DependedOnBy',
- 'new-MemberOf' => 'Parents',
- 'MemberOf-new' => 'Children',
- 'new-RefersTo' => 'RefersTo',
- 'RefersTo-new' => 'ReferredToBy',
- );
- foreach my $key ( keys %map ) {
- next unless $ARGS{$key};
- $create_args{ $map{$key} } = [ grep $_, split ' ', $ARGS{$key} ];
-
- }
-
- my ( $id, $Trans, $ErrMsg ) = $Ticket->Create(%create_args);
- unless ($id) {
- Abort($ErrMsg);
- }
-
- push( @Actions, split( "\n", $ErrMsg ) );
+ push ( @Actions, split("\n", $ErrMsg) );
unless ( $Ticket->CurrentUserHasRight('ShowTicket') ) {
- Abort( "No permission to view newly created ticket #" . $Ticket->id . "." );
+ Abort( "No permission to view newly created ticket #"
+ . $Ticket->id . "." );
}
return ( $Ticket, @Actions );
@@ -1028,130 +354,62 @@ sub LoadTicket {
# {{{ sub ProcessUpdateMessage
-=head2 ProcessUpdateMessage
-
-Takes paramhash with fields ARGSRef, TicketObj and SkipSignatureOnly.
-
-Don't write message if it only contains current user's signature and
-SkipSignatureOnly argument is true. Function anyway adds attachments
-and updates time worked field even if skips message. The default value
-is true.
-
-=cut
-
sub ProcessUpdateMessage {
+ #TODO document what else this takes.
my %args = (
- ARGSRef => undef,
- TicketObj => undef,
- SkipSignatureOnly => 1,
+ ARGSRef => undef,
+ Actions => undef,
+ TicketObj => undef,
@_
);
- if ( $args{ARGSRef}->{'UpdateAttachments'}
- && !keys %{ $args{ARGSRef}->{'UpdateAttachments'} } )
- {
- delete $args{ARGSRef}->{'UpdateAttachments'};
- }
+ #Make the update content have no 'weird' newlines in it
+ if ( $args{ARGSRef}->{'UpdateContent'} ) {
- # Strip the signature
- $args{ARGSRef}->{UpdateContent} = RT::Interface::Web::StripContent(
- Content => $args{ARGSRef}->{UpdateContent},
- ContentType => $args{ARGSRef}->{UpdateContentType},
- StripSignature => $args{SkipSignatureOnly},
- CurrentUser => $args{'TicketObj'}->CurrentUser,
- );
-
- # If, after stripping the signature, we have no message, move the
- # UpdateTimeWorked into adjusted TimeWorked, so that a later
- # ProcessBasics can deal -- then bail out.
- if ( not $args{ARGSRef}->{'UpdateAttachments'}
- and not length $args{ARGSRef}->{'UpdateContent'} )
- {
- if ( $args{ARGSRef}->{'UpdateTimeWorked'} ) {
- $args{ARGSRef}->{TimeWorked} = $args{TicketObj}->TimeWorked + delete $args{ARGSRef}->{'UpdateTimeWorked'};
+ if (
+ $args{ARGSRef}->{'UpdateSubject'} eq $args{'TicketObj'}->Subject() )
+ {
+ $args{ARGSRef}->{'UpdateSubject'} = undef;
}
- return;
- }
-
- if ( $args{ARGSRef}->{'UpdateSubject'} eq $args{'TicketObj'}->Subject ) {
- $args{ARGSRef}->{'UpdateSubject'} = undef;
- }
-
- my $Message = MakeMIMEEntity(
- Subject => $args{ARGSRef}->{'UpdateSubject'},
- Body => $args{ARGSRef}->{'UpdateContent'},
- Type => $args{ARGSRef}->{'UpdateContentType'},
- );
- $Message->head->add( 'Message-ID' => RT::Interface::Email::GenMessageId( Ticket => $args{'TicketObj'}, ) );
- my $old_txn = RT::Transaction->new( $session{'CurrentUser'} );
- if ( $args{ARGSRef}->{'QuoteTransaction'} ) {
- $old_txn->Load( $args{ARGSRef}->{'QuoteTransaction'} );
- } else {
- $old_txn = $args{TicketObj}->Transactions->First();
- }
-
- if ( my $msg = $old_txn->Message->First ) {
- RT::Interface::Email::SetInReplyTo(
- Message => $Message,
- InReplyTo => $msg
+ my $Message = MakeMIMEEntity(
+ Subject => $args{ARGSRef}->{'UpdateSubject'},
+ Body => $args{ARGSRef}->{'UpdateContent'},
);
- }
-
- if ( $args{ARGSRef}->{'UpdateAttachments'} ) {
- $Message->make_multipart;
- $Message->add_part($_) foreach values %{ $args{ARGSRef}->{'UpdateAttachments'} };
- }
- if ( $args{ARGSRef}->{'AttachTickets'} ) {
- require RT::Action::SendEmail;
- RT::Action::SendEmail->AttachTickets( RT::Action::SendEmail->AttachTickets,
- ref $args{ARGSRef}->{'AttachTickets'}
- ? @{ $args{ARGSRef}->{'AttachTickets'} }
- : ( $args{ARGSRef}->{'AttachTickets'} ) );
- }
-
- my $bcc = $args{ARGSRef}->{'UpdateBcc'};
- my $cc = $args{ARGSRef}->{'UpdateCc'};
-
- my %message_args = (
- CcMessageTo => $cc,
- BccMessageTo => $bcc,
- Sign => $args{ARGSRef}->{'Sign'},
- Encrypt => $args{ARGSRef}->{'Encrypt'},
- MIMEObj => $Message,
- TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'}
- );
-
- unless ( $args{'ARGSRef'}->{'UpdateIgnoreAddressCheckboxes'} ) {
- foreach my $key ( keys %{ $args{ARGSRef} } ) {
- next unless $key =~ /^Update(Cc|Bcc)-(.*)$/;
-
- my $var = ucfirst($1) . 'MessageTo';
- my $value = $2;
- if ( $message_args{$var} ) {
- $message_args{$var} .= ", $value";
- } else {
- $message_args{$var} = $value;
- }
+ if ($args{ARGSRef}->{'UpdateAttachments'}) {
+ $Message->make_multipart;
+ $Message->add_part($_) foreach values %{$args{ARGSRef}->{'UpdateAttachments'}};
}
- }
- my @results;
- if ( $args{ARGSRef}->{'UpdateType'} =~ /^(private|public)$/ ) {
- my ( $Transaction, $Description, $Object ) = $args{TicketObj}->Comment(%message_args);
- push( @results, $Description );
- $Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object;
- } elsif ( $args{ARGSRef}->{'UpdateType'} eq 'response' ) {
- my ( $Transaction, $Description, $Object ) = $args{TicketObj}->Correspond(%message_args);
- push( @results, $Description );
- $Object->UpdateCustomFields( ARGSRef => $args{ARGSRef} ) if $Object;
- } else {
- push( @results,
- loc("Update type was neither correspondence nor comment.") . " " . loc("Update not recorded.") );
+ ## TODO: Implement public comments
+ if ( $args{ARGSRef}->{'UpdateType'} =~ /^(private|public)$/ ) {
+ my ( $Transaction, $Description ) = $args{TicketObj}->Comment(
+ CcMessageTo => $args{ARGSRef}->{'UpdateCc'},
+ BccMessageTo => $args{ARGSRef}->{'UpdateBcc'},
+ MIMEObj => $Message,
+ TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'}
+ );
+ push ( @{ $args{Actions} }, $Description );
+ }
+ elsif ( $args{ARGSRef}->{'UpdateType'} eq 'response' ) {
+ my ( $Transaction, $Description ) = $args{TicketObj}->Correspond(
+ CcMessageTo => $args{ARGSRef}->{'UpdateCc'},
+ BccMessageTo => $args{ARGSRef}->{'UpdateBcc'},
+ MIMEObj => $Message,
+ TimeTaken => $args{ARGSRef}->{'UpdateTimeWorked'}
+ );
+ push ( @{ $args{Actions} }, $Description );
+ }
+ else {
+ push ( @{ $args{'Actions'} },
+ loc("Update type was neither correspondence nor comment.").
+ " ".
+ loc("Update not recorded.")
+ );
+ }
}
- return @results;
}
# }}}
@@ -1162,8 +420,6 @@ sub ProcessUpdateMessage {
Takes a paramhash Subject, Body and AttachmentFieldName.
-Also takes Form, Cc and Type as optional paramhash keys.
-
Returns a MIME::Entity.
=cut
@@ -1177,66 +433,291 @@ sub MakeMIMEEntity {
Cc => undef,
Body => undef,
AttachmentFieldName => undef,
- Type => undef,
- @_,
- );
- my $Message = MIME::Entity->build(
- Type => 'multipart/mixed',
- Subject => $args{'Subject'} || "",
- From => $args{'From'},
- Cc => $args{'Cc'},
+ map Encode::encode_utf8($_), @_,
);
- if ( defined $args{'Body'} && length $args{'Body'} ) {
-
- # Make the update content have no 'weird' newlines in it
- $args{'Body'} =~ s/\r\n/\n/gs;
+ #Make the update content have no 'weird' newlines in it
+ $args{'Body'} =~ s/\r\n/\n/gs;
+ my $Message;
+ {
# MIME::Head is not happy in utf-8 domain. This only happens
# when processing an incoming email (so far observed).
no utf8;
use bytes;
- $Message->attach(
- Type => $args{'Type'} || 'text/plain',
- Charset => 'UTF-8',
- Data => $args{'Body'},
+ $Message = MIME::Entity->build(
+ Subject => $args{'Subject'} || "",
+ From => $args{'From'},
+ Cc => $args{'Cc'},
+ Data => [ $args{'Body'} ]
+ );
+ }
+
+ my $cgi_object = $m->cgi_object;
+
+ if (my $filehandle = $cgi_object->upload( $args{'AttachmentFieldName'} ) ) {
+
+
+
+ use File::Temp qw(tempfile tempdir);
+
+ #foreach my $filehandle (@filenames) {
+
+ my ( $fh, $temp_file ) = tempfile();
+
+ binmode $fh; #thank you, windows
+ my ($buffer);
+ while ( my $bytesread = read( $filehandle, $buffer, 4096 ) ) {
+ print $fh $buffer;
+ }
+
+ my $uploadinfo = $cgi_object->uploadInfo($filehandle);
+
+ # Prefer the cached name first over CGI.pm stringification.
+ my $filename = $RT::Mason::CGI::Filename;
+ $filename = "$filehandle" unless defined($filename);
+
+ $filename =~ s#^.*[\\/]##;
+
+ $Message->attach(
+ Path => $temp_file,
+ Filename => $filename,
+ Type => $uploadinfo->{'Content-Type'},
+ );
+ close($fh);
+
+ # }
+
+ }
+
+ $Message->make_singlepart();
+ RT::I18N::SetMIMEEntityToUTF8($Message); # convert text parts into utf-8
+
+ return ($Message);
+
+}
+
+# }}}
+
+# {{{ sub ProcessSearchQuery
+
+=head2 ProcessSearchQuery
+
+ Takes a form such as the one filled out in webrt/Search/Elements/PickRestriction and turns it into something that RT::Tickets can understand.
+
+TODO Doc exactly what comes in the paramhash
+
+
+=cut
+
+sub ProcessSearchQuery {
+ my %args = @_;
+
+ ## TODO: The only parameter here is %ARGS. Maybe it would be
+ ## cleaner to load this parameter as $ARGS, and use $ARGS->{...}
+ ## instead of $args{ARGS}->{...} ? :)
+
+ #Searches are sticky.
+ if ( defined $session{'tickets'} ) {
+
+ # Reset the old search
+ $session{'tickets'}->GotoFirstItem;
+ }
+ else {
+
+ # Init a new search
+ $session{'tickets'} = RT::Tickets->new( $session{'CurrentUser'} );
+ }
+
+ #Import a bookmarked search if we have one
+ if ( defined $args{ARGS}->{'Bookmark'} ) {
+ $session{'tickets'}->ThawLimits( $args{ARGS}->{'Bookmark'} );
+ }
+
+ # {{{ Goto next/prev page
+ if ( $args{ARGS}->{'GotoPage'} eq 'Next' ) {
+ $session{'tickets'}->NextPage;
+ }
+ elsif ( $args{ARGS}->{'GotoPage'} eq 'Prev' ) {
+ $session{'tickets'}->PrevPage;
+ }
+ elsif ( $args{ARGS}->{'GotoPage'} > 0 ) {
+ $session{'tickets'}->GotoPage( $args{ARGS}->{GotoPage} - 1 );
+ }
+
+ # }}}
+
+ # {{{ Deal with limiting the search
+
+ if ( $args{ARGS}->{'RefreshSearchInterval'} ) {
+ $session{'tickets_refresh_interval'} =
+ $args{ARGS}->{'RefreshSearchInterval'};
+ }
+
+ if ( $args{ARGS}->{'TicketsSortBy'} ) {
+ $session{'tickets_sort_by'} = $args{ARGS}->{'TicketsSortBy'};
+ $session{'tickets_sort_order'} = $args{ARGS}->{'TicketsSortOrder'};
+ $session{'tickets'}->OrderBy(
+ FIELD => $args{ARGS}->{'TicketsSortBy'},
+ ORDER => $args{ARGS}->{'TicketsSortOrder'}
+ );
+ }
+
+ # }}}
+
+ # {{{ Set the query limit
+ if ( defined $args{ARGS}->{'RowsPerPage'} ) {
+ $RT::Logger->debug(
+ "limiting to " . $args{ARGS}->{'RowsPerPage'} . " rows" );
+
+ $session{'tickets_rows_per_page'} = $args{ARGS}->{'RowsPerPage'};
+ $session{'tickets'}->RowsPerPage( $args{ARGS}->{'RowsPerPage'} );
+ }
+
+ # }}}
+ # {{{ Limit priority
+ if ( $args{ARGS}->{'ValueOfPriority'} ne '' ) {
+ $session{'tickets'}->LimitPriority(
+ VALUE => $args{ARGS}->{'ValueOfPriority'},
+ OPERATOR => $args{ARGS}->{'PriorityOp'}
+ );
+ }
+
+ # }}}
+ # {{{ Limit owner
+ if ( $args{ARGS}->{'ValueOfOwner'} ne '' ) {
+ $session{'tickets'}->LimitOwner(
+ VALUE => $args{ARGS}->{'ValueOfOwner'},
+ OPERATOR => $args{ARGS}->{'OwnerOp'}
);
}
- if ( $args{'AttachmentFieldName'} ) {
+ # }}}
+ # {{{ Limit requestor email
- my $cgi_object = $m->cgi_object;
+ if ( $args{ARGS}->{'ValueOfRequestor'} ne '' ) {
+ my $alias = $session{'tickets'}->LimitRequestor(
+ VALUE => $args{ARGS}->{'ValueOfRequestor'},
+ OPERATOR => $args{ARGS}->{'RequestorOp'},
+ );
- if ( my $filehandle = $cgi_object->upload( $args{'AttachmentFieldName'} ) ) {
+ }
+
+ # }}}
+ # {{{ Limit Queue
+ if ( $args{ARGS}->{'ValueOfQueue'} ne '' ) {
+ $session{'tickets'}->LimitQueue(
+ VALUE => $args{ARGS}->{'ValueOfQueue'},
+ OPERATOR => $args{ARGS}->{'QueueOp'}
+ );
+ }
- my ( @content, $buffer );
- while ( my $bytesread = read( $filehandle, $buffer, 4096 ) ) {
- push @content, $buffer;
+ # }}}
+ # {{{ Limit Status
+ if ( $args{ARGS}->{'ValueOfStatus'} ne '' ) {
+ if ( ref( $args{ARGS}->{'ValueOfStatus'} ) ) {
+ foreach my $value ( @{ $args{ARGS}->{'ValueOfStatus'} } ) {
+ $session{'tickets'}->LimitStatus(
+ VALUE => $value,
+ OPERATOR => $args{ARGS}->{'StatusOp'},
+ );
}
+ }
+ else {
+ $session{'tickets'}->LimitStatus(
+ VALUE => $args{ARGS}->{'ValueOfStatus'},
+ OPERATOR => $args{ARGS}->{'StatusOp'},
+ );
+ }
+
+ }
- my $uploadinfo = $cgi_object->uploadInfo($filehandle);
+ # }}}
+ # {{{ Limit Subject
+ if ( $args{ARGS}->{'ValueOfSubject'} ne '' ) {
+ my $val = $args{ARGS}->{'ValueOfSubject'};
+ if ($args{ARGS}->{'SubjectOp'} =~ /like/) {
+ $val = "%".$val."%";
+ }
+ $session{'tickets'}->LimitSubject(
+ VALUE => $val,
+ OPERATOR => $args{ARGS}->{'SubjectOp'},
+ );
+ }
- # Prefer the cached name first over CGI.pm stringification.
- my $filename = $RT::Mason::CGI::Filename;
- $filename = "$filehandle" unless defined($filename);
- $filename = Encode::decode_utf8($filename);
- $filename =~ s{^.*[\\/]}{};
+ # }}}
+ # {{{ Limit Dates
+ if ( $args{ARGS}->{'ValueOfDate'} ne '' ) {
+ my $date = ParseDateToISO( $args{ARGS}->{'ValueOfDate'} );
+ $args{ARGS}->{'DateType'} =~ s/_Date$//;
- $Message->attach(
- Type => $uploadinfo->{'Content-Type'},
- Filename => $filename,
- Data => \@content,
+ if ( $args{ARGS}->{'DateType'} eq 'Updated' ) {
+ $session{'tickets'}->LimitTransactionDate(
+ VALUE => $date,
+ OPERATOR => $args{ARGS}->{'DateOp'},
);
- if ( !$args{'Subject'} && !( defined $args{'Body'} && length $args{'Body'} ) ) {
- $Message->head->set( 'Subject' => $filename );
+ }
+ else {
+ $session{'tickets'}->LimitDate( FIELD => $args{ARGS}->{'DateType'},
+ VALUE => $date,
+ OPERATOR => $args{ARGS}->{'DateOp'},
+ );
+ }
+ }
+
+ # }}}
+ # {{{ Limit Content
+ if ( $args{ARGS}->{'ValueOfAttachmentField'} ne '' ) {
+ my $val = $args{ARGS}->{'ValueOfAttachmentField'};
+ if ($args{ARGS}->{'AttachmentFieldOp'} =~ /like/) {
+ $val = "%".$val."%";
+ }
+ $session{'tickets'}->Limit(
+ FIELD => $args{ARGS}->{'AttachmentField'},
+ VALUE => $val,
+ OPERATOR => $args{ARGS}->{'AttachmentFieldOp'},
+ );
+ }
+
+ # }}}
+
+ # {{{ Limit CustomFields
+
+ foreach my $arg ( keys %{ $args{ARGS} } ) {
+ my $id;
+ if ( $arg =~ /^CustomField(\d+)$/ ) {
+ $id = $1;
+ }
+ else {
+ next;
+ }
+ next unless ( $args{ARGS}->{$arg} );
+
+ my $form = $args{ARGS}->{$arg};
+ my $oper = $args{ARGS}->{ "CustomFieldOp" . $id };
+ foreach my $value ( ref($form) ? @{$form} : ($form) ) {
+ my $quote = 1;
+ if ($oper =~ /like/i) {
+ $value = "%".$value."%";
}
+ if ( $value =~ /^null$/i ) {
+
+ #Don't quote the string 'null'
+ $quote = 0;
+
+ # Convert the operator to something apropriate for nulls
+ $oper = 'IS' if ( $oper eq '=' );
+ $oper = 'IS NOT' if ( $oper eq '!=' );
+ }
+ $session{'tickets'}->LimitCustomField( CUSTOMFIELD => $id,
+ OPERATOR => $oper,
+ QUOTEVALUE => $quote,
+ VALUE => $value );
}
}
- $Message->make_singlepart;
- RT::I18N::SetMIMEEntityToUTF8($Message); # convert text parts into utf-8
+ # }}}
- return ($Message);
}
@@ -1254,7 +735,7 @@ Returns an ISO date and time in GMT
sub ParseDateToISO {
my $date = shift;
- my $date_obj = RT::Date->new( $session{'CurrentUser'} );
+ my $date_obj = RT::Date->new($session{'CurrentUser'});
$date_obj->Set(
Format => 'unknown',
Value => $date
@@ -1264,56 +745,98 @@ sub ParseDateToISO {
# }}}
+# {{{ sub Config
+# TODO: This might eventually read the cookies, user configuration
+# information from the DB, queue configuration information from the
+# DB, etc.
+
+sub Config {
+ my $args = shift;
+ my $key = shift;
+ return $args->{$key} || $RT::WebOptions{$key};
+}
+
+# }}}
+
# {{{ sub ProcessACLChanges
sub ProcessACLChanges {
my $ARGSref = shift;
- #XXX: why don't we get ARGSref like in other Process* subs?
+ my %ARGS = %$ARGSref;
- my @results;
+ my ( $ACL, @results );
- foreach my $arg ( keys %$ARGSref ) {
- next unless ( $arg =~ /^(GrantRight|RevokeRight)-(\d+)-(.+?)-(\d+)$/ );
- my ( $method, $principal_id, $object_type, $object_id ) = ( $1, $2, $3, $4 );
+ foreach my $arg (keys %ARGS) {
+ if ($arg =~ /GrantRight-(\d+)-(.*?)-(\d+)$/) {
+ my $principal_id = $1;
+ my $object_type = $2;
+ my $object_id = $3;
+ my $rights = $ARGS{$arg};
- my @rights;
- if ( UNIVERSAL::isa( $ARGSref->{$arg}, 'ARRAY' ) ) {
- @rights = @{ $ARGSref->{$arg} };
- } else {
- @rights = $ARGSref->{$arg};
+ my $principal = RT::Principal->new($session{'CurrentUser'});
+ $principal->Load($principal_id);
+
+ my $obj;
+
+ if ($object_type eq 'RT::Queue') {
+ $obj = RT::Queue->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+ } elsif ($object_type eq 'RT::Group') {
+ $obj = RT::Group->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+
+ } elsif ($object_type eq 'RT::System') {
+ $obj = $RT::System;
+ } else {
+ push (@results, loc("System Error").
+ loc("Rights could not be granted for [_1]", $object_type));
+ next;
+ }
+
+ my @rights = ref($ARGS{$arg}) eq 'ARRAY' ? @{$ARGS{$arg}} : ($ARGS{$arg});
+ foreach my $right (@rights) {
+ next unless ($right);
+ my ($val, $msg) = $principal->GrantRight(Object => $obj, Right => $right);
+ push (@results, $msg);
+ }
}
- @rights = grep $_, @rights;
- next unless @rights;
-
- my $principal = RT::Principal->new( $session{'CurrentUser'} );
- $principal->Load($principal_id);
-
- my $obj;
- if ( $object_type eq 'RT::System' ) {
- $obj = $RT::System;
- } elsif ( $RT::ACE::OBJECT_TYPES{$object_type} ) {
- $obj = $object_type->new( $session{'CurrentUser'} );
- $obj->Load($object_id);
- unless ( $obj->id ) {
- $RT::Logger->error("couldn't load $object_type #$object_id");
+ elsif ($arg =~ /RevokeRight-(\d+)-(.*?)-(\d+)-(.*?)$/) {
+ my $principal_id = $1;
+ my $object_type = $2;
+ my $object_id = $3;
+ my $right = $4;
+
+ my $principal = RT::Principal->new($session{'CurrentUser'});
+ $principal->Load($principal_id);
+ next unless ($right);
+ my $obj;
+
+ if ($object_type eq 'RT::Queue') {
+ $obj = RT::Queue->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+ } elsif ($object_type eq 'RT::Group') {
+ $obj = RT::Group->new($session{'CurrentUser'});
+ $obj->Load($object_id);
+
+ } elsif ($object_type eq 'RT::System') {
+ $obj = $RT::System;
+ } else {
+ push (@results, loc("System Error").
+ loc("Rights could not be revoked for [_1]", $object_type));
next;
}
- } else {
- $RT::Logger->error("object type '$object_type' is incorrect");
- push( @results, loc("System Error") . ': ' . loc( "Rights could not be granted for [_1]", $object_type ) );
- next;
+ my ($val, $msg) = $principal->RevokeRight(Object => $obj, Right => $right);
+ push (@results, $msg);
}
- foreach my $right (@rights) {
- my ( $val, $msg ) = $principal->$method( Object => $obj, Right => $right );
- push( @results, $msg );
- }
+
}
return (@results);
-}
+
+ }
# }}}
@@ -1329,20 +852,59 @@ Returns an array of success/failure messages
sub UpdateRecordObject {
my %args = (
- ARGSRef => undef,
- AttributesRef => undef,
- Object => undef,
+ ARGSRef => undef,
+ AttributesRef => undef,
+ Object => undef,
AttributePrefix => undef,
@_
);
- my $Object = $args{'Object'};
- my @results = $Object->Update(
- AttributesRef => $args{'AttributesRef'},
- ARGSRef => $args{'ARGSRef'},
- AttributePrefix => $args{'AttributePrefix'},
- );
+ my (@results);
+
+ my $object = $args{'Object'};
+ my $attributes = $args{'AttributesRef'};
+ my $ARGSRef = $args{'ARGSRef'};
+ foreach my $attribute (@$attributes) {
+ my $value;
+ if ( defined $ARGSRef->{$attribute} ) {
+ $value = $ARGSRef->{$attribute};
+ }
+ elsif (
+ defined( $args{'AttributePrefix'} )
+ && defined(
+ $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute }
+ )
+ ) {
+ $value = $ARGSRef->{ $args{'AttributePrefix'} . "-" . $attribute };
+ } else {
+ next;
+ }
+
+ $value =~ s/\r\n/\n/gs;
+
+ if ($value ne $object->$attribute()){
+
+ my $method = "Set$attribute";
+ my ( $code, $msg ) = $object->$method($value);
+
+ push @results, loc($attribute) . ': ' . loc_fuzzy($msg);
+=for loc
+ "[_1] could not be set to [_2].", # loc
+ "That is already the current value", # loc
+ "No value sent to _Set!\n", # loc
+ "Illegal value for [_1]", # loc
+ "The new value has been set.", # loc
+ "No column specified", # loc
+ "Immutable field", # loc
+ "Nonexistant field?", # loc
+ "Invalid data", # loc
+ "Couldn't find row", # loc
+ "Missing a primary key?: [_1]", # loc
+ "Found Object", # loc
+=cut
+ };
+ }
return (@results);
}
@@ -1360,44 +922,37 @@ sub ProcessCustomFieldUpdates {
my $Object = $args{'CustomFieldObj'};
my $ARGSRef = $args{'ARGSRef'};
- my @attribs = qw(Name Type Description Queue SortOrder);
+ my @attribs = qw( Name Type Description Queue SortOrder);
my @results = UpdateRecordObject(
AttributesRef => \@attribs,
Object => $Object,
ARGSRef => $ARGSRef
);
- my $prefix = "CustomField-" . $Object->Id;
- if ( $ARGSRef->{"$prefix-AddValue-Name"} ) {
+ if ( $ARGSRef->{ "CustomField-" . $Object->Id . "-AddValue-Name" } ) {
+
my ( $addval, $addmsg ) = $Object->AddValue(
- Name => $ARGSRef->{"$prefix-AddValue-Name"},
- Description => $ARGSRef->{"$prefix-AddValue-Description"},
- SortOrder => $ARGSRef->{"$prefix-AddValue-SortOrder"},
+ Name =>
+ $ARGSRef->{ "CustomField-" . $Object->Id . "-AddValue-Name" },
+ Description => $ARGSRef->{ "CustomField-"
+ . $Object->Id
+ . "-AddValue-Description" },
+ SortOrder => $ARGSRef->{ "CustomField-"
+ . $Object->Id
+ . "-AddValue-SortOrder" },
);
- push( @results, $addmsg );
+ push ( @results, $addmsg );
}
-
- my @delete_values
- = ( ref $ARGSRef->{"$prefix-DeleteValue"} eq 'ARRAY' )
- ? @{ $ARGSRef->{"$prefix-DeleteValue"} }
- : ( $ARGSRef->{"$prefix-DeleteValue"} );
-
+ my @delete_values = (
+ ref $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } eq
+ 'ARRAY' )
+ ? @{ $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } }
+ : ( $ARGSRef->{ 'CustomField-' . $Object->Id . '-DeleteValue' } );
foreach my $id (@delete_values) {
next unless defined $id;
my ( $err, $msg ) = $Object->DeleteValue($id);
- push( @results, $msg );
+ push ( @results, $msg );
}
-
- my $vals = $Object->Values();
- while ( my $cfv = $vals->Next() ) {
- if ( my $so = $ARGSRef->{ "$prefix-SortOrder" . $cfv->Id } ) {
- if ( $cfv->SortOrder != $so ) {
- my ( $err, $msg ) = $cfv->SetSortOrder($so);
- push( @results, $msg );
- }
- }
- }
-
return (@results);
}
@@ -1422,35 +977,30 @@ sub ProcessTicketBasics {
my $TicketObj = $args{'TicketObj'};
my $ARGSRef = $args{'ARGSRef'};
- # {{{ Set basic fields
+ # {{{ Set basic fields
my @attribs = qw(
- Subject
- FinalPriority
- Priority
- TimeEstimated
- TimeWorked
- TimeLeft
- Type
- Status
- Queue
+ Subject
+ FinalPriority
+ Priority
+ TimeEstimated
+ TimeWorked
+ TimeLeft
+ Status
+ Queue
);
if ( $ARGSRef->{'Queue'} and ( $ARGSRef->{'Queue'} !~ /^(\d+)$/ ) ) {
my $tempqueue = RT::Queue->new($RT::SystemUser);
$tempqueue->Load( $ARGSRef->{'Queue'} );
if ( $tempqueue->id ) {
- $ARGSRef->{'Queue'} = $tempqueue->id;
+ $ARGSRef->{'Queue'} = $tempqueue->Id();
}
}
- # Status isn't a field that can be set to a null value.
- # RT core complains if you try
- delete $ARGSRef->{'Status'} unless $ARGSRef->{'Status'};
-
my @results = UpdateRecordObject(
AttributesRef => \@attribs,
Object => $TicketObj,
- ARGSRef => $ARGSRef,
+ ARGSRef => $ARGSRef
);
# We special case owner changing, so we can use ForceOwnerChange
@@ -1458,12 +1008,14 @@ sub ProcessTicketBasics {
my ($ChownType);
if ( $ARGSRef->{'ForceOwnerChange'} ) {
$ChownType = "Force";
- } else {
+ }
+ else {
$ChownType = "Give";
}
- my ( $val, $msg ) = $TicketObj->SetOwner( $ARGSRef->{'Owner'}, $ChownType );
- push( @results, $msg );
+ my ( $val, $msg ) =
+ $TicketObj->SetOwner( $ARGSRef->{'Owner'}, $ChownType );
+ push ( @results, $msg );
}
# }}}
@@ -1473,217 +1025,142 @@ sub ProcessTicketBasics {
# }}}
-sub ProcessTicketCustomFieldUpdates {
- my %args = @_;
- $args{'Object'} = delete $args{'TicketObj'};
- my $ARGSRef = { %{ $args{'ARGSRef'} } };
+# {{{ Sub ProcessTicketCustomFieldUpdates
- # Build up a list of objects that we want to work with
- my %custom_fields_to_mod;
- foreach my $arg ( keys %$ARGSRef ) {
- if ( $arg =~ /^Ticket-(\d+-.*)/ ) {
- $ARGSRef->{"Object-RT::Ticket-$1"} = delete $ARGSRef->{$arg};
- } elsif ( $arg =~ /^CustomField-(\d+-.*)/ ) {
- $ARGSRef->{"Object-RT::Ticket--$1"} = delete $ARGSRef->{$arg};
- }
- }
+sub ProcessTicketCustomFieldUpdates {
+ my %args = (
+ ARGSRef => undef,
+ @_
+ );
- return ProcessObjectCustomFieldUpdates( %args, ARGSRef => $ARGSRef );
-}
+ my @results;
-sub ProcessObjectCustomFieldUpdates {
- my %args = @_;
my $ARGSRef = $args{'ARGSRef'};
- my @results;
- # Build up a list of objects that we want to work with
+ # Build up a list of tickets that we want to work with
+ my %tickets_to_mod;
my %custom_fields_to_mod;
- foreach my $arg ( keys %$ARGSRef ) {
-
- # format: Object-<object class>-<object id>-CustomField-<CF id>-<commands>
- next unless $arg =~ /^Object-([\w:]+)-(\d*)-CustomField-(\d+)-(.*)$/;
+ foreach my $arg ( keys %{$ARGSRef} ) {
+ if ( $arg =~ /^Ticket-(\d+)-CustomField-(\d+)-/ ) {
- # For each of those objects, find out what custom fields we want to work with.
- $custom_fields_to_mod{$1}{ $2 || 0 }{$3}{$4} = $ARGSRef->{$arg};
- }
-
- # For each of those objects
- foreach my $class ( keys %custom_fields_to_mod ) {
- foreach my $id ( keys %{ $custom_fields_to_mod{$class} } ) {
- my $Object = $args{'Object'};
- $Object = $class->new( $session{'CurrentUser'} )
- unless $Object && ref $Object eq $class;
-
- $Object->Load($id) unless ( $Object->id || 0 ) == $id;
- unless ( $Object->id ) {
- $RT::Logger->warning("Couldn't load object $class #$id");
- next;
- }
-
- foreach my $cf ( keys %{ $custom_fields_to_mod{$class}{$id} } ) {
- my $CustomFieldObj = RT::CustomField->new( $session{'CurrentUser'} );
- $CustomFieldObj->LoadById($cf);
- unless ( $CustomFieldObj->id ) {
- $RT::Logger->warning("Couldn't load custom field #$cf");
- next;
- }
- push @results,
- _ProcessObjectCustomFieldUpdates(
- Prefix => "Object-$class-$id-CustomField-$cf-",
- Object => $Object,
- CustomField => $CustomFieldObj,
- ARGS => $custom_fields_to_mod{$class}{$id}{$cf},
- );
- }
+ # For each of those tickets, find out what custom fields we want to work with.
+ $custom_fields_to_mod{$1}{$2} = 1;
}
}
- return @results;
-}
-
-sub _ProcessObjectCustomFieldUpdates {
- my %args = @_;
- my $cf = $args{'CustomField'};
- my $cf_type = $cf->Type;
-
- # Remove blank Values since the magic field will take care of this. Sometimes
- # the browser gives you a blank value which causes CFs to be processed twice
- if ( defined $args{'ARGS'}->{'Values'}
- && !length $args{'ARGS'}->{'Values'}
- && $args{'ARGS'}->{'Values-Magic'} )
- {
- delete $args{'ARGS'}->{'Values'};
- }
-
- my @results;
- foreach my $arg ( keys %{ $args{'ARGS'} } ) {
-
- # skip category argument
- next if $arg eq 'Category';
- # since http won't pass in a form element with a null value, we need
- # to fake it
- if ( $arg eq 'Values-Magic' ) {
+ # For each of those tickets
+ foreach my $tick ( keys %custom_fields_to_mod ) {
+ my $Ticket = RT::Ticket->new( $session{'CurrentUser'} );
+ $Ticket->Load($tick);
- # We don't care about the magic, if there's really a values element;
- next if defined $args{'ARGS'}->{'Value'} && length $args{'ARGS'}->{'Value'};
- next if defined $args{'ARGS'}->{'Values'} && length $args{'ARGS'}->{'Values'};
+ # For each custom field
+ foreach my $cf ( keys %{ $custom_fields_to_mod{$tick} } ) {
- # "Empty" values does not mean anything for Image and Binary fields
- next if $cf_type =~ /^(?:Image|Binary)$/;
+ my $CustomFieldObj = RT::CustomField->new($session{'CurrentUser'});
+ $CustomFieldObj->LoadById($cf);
- $arg = 'Values';
- $args{'ARGS'}->{'Values'} = undef;
- }
-
- my @values = ();
- if ( ref $args{'ARGS'}->{$arg} eq 'ARRAY' ) {
- @values = @{ $args{'ARGS'}->{$arg} };
- } elsif ( $cf_type =~ /text/i ) { # Both Text and Wikitext
- @values = ( $args{'ARGS'}->{$arg} );
- } else {
- @values = split /\r*\n/, $args{'ARGS'}->{$arg}
- if defined $args{'ARGS'}->{$arg};
- }
- @values = grep length, map {
- s/\r+\n/\n/g;
- s/^\s+//;
- s/\s+$//;
- $_;
- }
- grep defined, @values;
+ foreach my $arg ( keys %{$ARGSRef} ) {
+ # since http won't pass in a form element with a null value, we need
+ # to fake it
+ if ($arg =~ /^(.*?)-Values-Magic$/ ) {
+ # We don't care about the magic, if there's really a values element;
+ next if (exists $ARGSRef->{$1.'-Values'}) ;
- if ( $arg eq 'AddValue' || $arg eq 'Value' ) {
- foreach my $value (@values) {
- my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue(
- Field => $cf->id,
- Value => $value
- );
- push( @results, $msg );
- }
- } elsif ( $arg eq 'Upload' ) {
- my $value_hash = _UploadedFile( $args{'Prefix'} . $arg ) or next;
- my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue( %$value_hash, Field => $cf, );
- push( @results, $msg );
- } elsif ( $arg eq 'DeleteValues' ) {
- foreach my $value (@values) {
- my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue(
- Field => $cf,
- Value => $value,
- );
- push( @results, $msg );
- }
- } elsif ( $arg eq 'DeleteValueIds' ) {
- foreach my $value (@values) {
- my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue(
- Field => $cf,
- ValueId => $value,
- );
- push( @results, $msg );
- }
- } elsif ( $arg eq 'Values' && !$cf->Repeated ) {
- my $cf_values = $args{'Object'}->CustomFieldValues( $cf->id );
-
- my %values_hash;
- foreach my $value (@values) {
- if ( my $entry = $cf_values->HasEntry($value) ) {
- $values_hash{ $entry->id } = 1;
- next;
+ $arg = $1."-Values";
+ $ARGSRef->{$1."-Values"} = undef;
+
}
-
- my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue(
- Field => $cf,
- Value => $value
- );
- push( @results, $msg );
- $values_hash{$val} = 1 if $val;
- }
-
- $cf_values->RedoSearch;
- while ( my $cf_value = $cf_values->Next ) {
- next if $values_hash{ $cf_value->id };
-
- my ( $val, $msg ) = $args{'Object'}->DeleteCustomFieldValue(
- Field => $cf,
- ValueId => $cf_value->id
- );
- push( @results, $msg );
- }
- } elsif ( $arg eq 'Values' ) {
- my $cf_values = $args{'Object'}->CustomFieldValues( $cf->id );
-
- # keep everything up to the point of difference, delete the rest
- my $delete_flag;
- foreach my $old_cf ( @{ $cf_values->ItemsArrayRef } ) {
- if ( !$delete_flag and @values and $old_cf->Content eq $values[0] ) {
- shift @values;
- next;
+ next unless ( $arg =~ /^Ticket-$tick-CustomField-$cf-/ );
+ my @values =
+ ( ref( $ARGSRef->{$arg} ) eq 'ARRAY' )
+ ? @{ $ARGSRef->{$arg} }
+ : ( $ARGSRef->{$arg} );
+ if ( ( $arg =~ /-AddValue$/ ) || ( $arg =~ /-Value$/ ) ) {
+ foreach my $value (@values) {
+ next unless ($value);
+ my ( $val, $msg ) = $Ticket->AddCustomFieldValue(
+ Field => $cf,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+ }
+ elsif ( $arg =~ /-DeleteValues$/ ) {
+ foreach my $value (@values) {
+ next unless ($value);
+ my ( $val, $msg ) = $Ticket->DeleteCustomFieldValue(
+ Field => $cf,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+ }
+ elsif ( $arg =~ /-Values$/ and $CustomFieldObj->Type !~ /Entry/) {
+ my $cf_values = $Ticket->CustomFieldValues($cf);
+
+ my %values_hash;
+ foreach my $value (@values) {
+ next unless ($value);
+
+ # build up a hash of values that the new set has
+ $values_hash{$value} = 1;
+
+ unless ( $cf_values->HasEntry($value) ) {
+ my ( $val, $msg ) = $Ticket->AddCustomFieldValue(
+ Field => $cf,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+
+ }
+ while ( my $cf_value = $cf_values->Next ) {
+ unless ( $values_hash{ $cf_value->Content } == 1 ) {
+ my ( $val, $msg ) = $Ticket->DeleteCustomFieldValue(
+ Field => $cf,
+ Value => $cf_value->Content
+ );
+ push ( @results, $msg);
+
+ }
+
+ }
+ }
+ elsif ( $arg =~ /-Values$/ ) {
+ my $cf_values = $Ticket->CustomFieldValues($cf);
+
+ # keep everything up to the point of difference, delete the rest
+ my $delete_flag;
+ foreach my $old_cf (@{$cf_values->ItemsArrayRef}) {
+ if (!$delete_flag and @values and $old_cf->Content eq $values[0]) {
+ shift @values;
+ next;
+ }
+
+ $delete_flag ||= 1;
+ $old_cf->Delete;
+ }
+
+ # now add/replace extra things, if any
+ foreach my $value (@values) {
+ my ( $val, $msg ) = $Ticket->AddCustomFieldValue(
+ Field => $cf,
+ Value => $value
+ );
+ push ( @results, $msg );
+ }
+ }
+ else {
+ push ( @results, "User asked for an unknown update type for custom field " . $cf->Name . " for ticket " . $Ticket->id );
}
-
- $delete_flag ||= 1;
- $old_cf->Delete;
- }
-
- # now add/replace extra things, if any
- foreach my $value (@values) {
- my ( $val, $msg ) = $args{'Object'}->AddCustomFieldValue(
- Field => $cf,
- Value => $value
- );
- push( @results, $msg );
}
- } else {
- push(
- @results,
- loc("User asked for an unknown update type for custom field [_1] for [_2] object #[_3]",
- $cf->Name, ref $args{'Object'},
- $args{'Object'}->id
- )
- );
}
+ return (@results);
}
- return @results;
}
+# }}}
+
# {{{ sub ProcessTicketWatchers
=head2 ProcessTicketWatchers ( TicketObj => $Ticket, ARGSRef => \%ARGS );
@@ -1703,31 +1180,29 @@ sub ProcessTicketWatchers {
my $Ticket = $args{'TicketObj'};
my $ARGSRef = $args{'ARGSRef'};
- # Munge watchers
+ # {{{ Munge watchers
foreach my $key ( keys %$ARGSRef ) {
- # Delete deletable watchers
- if ( $key =~ /^Ticket-DeleteWatcher-Type-(.*)-Principal-(\d+)$/ ) {
- my ( $code, $msg ) = $Ticket->DeleteWatcher(
- PrincipalId => $2,
- Type => $1
- );
+ # {{{ Delete deletable watchers
+ if ( ( $key =~ /^Ticket-DelWatcher-Type-(.*)-Principal-(\d+)$/ ) ) {
+ my ( $code, $msg ) =
+ $Ticket->DeleteWatcher(PrincipalId => $2,
+ Type => $1);
push @results, $msg;
}
# Delete watchers in the simple style demanded by the bulk manipulator
elsif ( $key =~ /^Delete(Requestor|Cc|AdminCc)$/ ) {
- my ( $code, $msg ) = $Ticket->DeleteWatcher(
- Email => $ARGSRef->{$key},
- Type => $1
- );
+ my ( $code, $msg ) = $Ticket->DeleteWatcher( Type => $ARGSRef->{$key}, PrincipalId => $1 );
push @results, $msg;
}
- # Add new wathchers by email address
- elsif ( ( $ARGSRef->{$key} || '' ) =~ /^(?:AdminCc|Cc|Requestor)$/
- and $key =~ /^WatcherTypeEmail(\d*)$/ )
+ # }}}
+
+ # Add new wathchers by email address
+ elsif ( ( $ARGSRef->{$key} =~ /^(AdminCc|Cc|Requestor)$/ )
+ and ( $key =~ /^WatcherTypeEmail(\d*)$/ ) )
{
#They're in this order because otherwise $1 gets clobbered :/
@@ -1748,21 +1223,18 @@ sub ProcessTicketWatchers {
}
# Add new watchers by owner
- elsif ( $key =~ /^Ticket-AddWatcher-Principal-(\d*)$/ ) {
- my $principal_id = $1;
- my $form = $ARGSRef->{$key};
- foreach my $value ( ref($form) ? @{$form} : ($form) ) {
- next unless $value =~ /^(?:AdminCc|Cc|Requestor)$/i;
+ elsif ( ( $ARGSRef->{$key} =~ /^(AdminCc|Cc|Requestor)$/ )
+ and ( $key =~ /^Ticket-AddWatcher-Principal-(\d*)$/ ) ) {
- my ( $code, $msg ) = $Ticket->AddWatcher(
- Type => $value,
- PrincipalId => $principal_id
- );
- push @results, $msg;
- }
+ #They're in this order because otherwise $1 gets clobbered :/
+ my ( $code, $msg ) =
+ $Ticket->AddWatcher( Type => $ARGSRef->{$key}, PrincipalId => $1 );
+ push @results, $msg;
}
-
}
+
+ # }}}
+
return (@results);
}
@@ -1790,33 +1262,33 @@ sub ProcessTicketDates {
# {{{ Set date fields
my @date_fields = qw(
- Told
- Resolved
- Starts
- Started
- Due
+ Told
+ Resolved
+ Starts
+ Started
+ Due
);
#Run through each field in this list. update the value if apropriate
foreach my $field (@date_fields) {
- next unless exists $ARGSRef->{ $field . '_Date' };
- next if $ARGSRef->{ $field . '_Date' } eq '';
-
my ( $code, $msg );
my $DateObj = RT::Date->new( $session{'CurrentUser'} );
- $DateObj->Set(
- Format => 'unknown',
- Value => $ARGSRef->{ $field . '_Date' }
- );
- my $obj = $field . "Obj";
- if ( ( defined $DateObj->Unix )
- and ( $DateObj->Unix != $Ticket->$obj()->Unix() ) )
- {
- my $method = "Set$field";
- my ( $code, $msg ) = $Ticket->$method( $DateObj->ISO );
- push @results, "$msg";
+ #If it's something other than just whitespace
+ if ( $ARGSRef->{ $field . '_Date' } ne '' ) {
+ $DateObj->Set(
+ Format => 'unknown',
+ Value => $ARGSRef->{ $field . '_Date' }
+ );
+ my $obj = $field . "Obj";
+ if ( ( defined $DateObj->Unix )
+ and ( $DateObj->Unix ne $Ticket->$obj()->Unix() ) )
+ {
+ my $method = "Set$field";
+ my ( $code, $msg ) = $Ticket->$method( $DateObj->ISO );
+ push @results, "$msg";
+ }
}
}
@@ -1835,39 +1307,13 @@ Returns an array of results messages.
=cut
sub ProcessTicketLinks {
- my %args = (
- TicketObj => undef,
- ARGSRef => undef,
- @_
- );
+ my %args = ( TicketObj => undef,
+ ARGSRef => undef,
+ @_ );
my $Ticket = $args{'TicketObj'};
my $ARGSRef = $args{'ARGSRef'};
- my (@results) = ProcessRecordLinks( RecordObj => $Ticket, ARGSRef => $ARGSRef );
-
- #Merge if we need to
- if ( $ARGSRef->{ $Ticket->Id . "-MergeInto" } ) {
- $ARGSRef->{ $Ticket->Id . "-MergeInto" } =~ s/\s+//g;
- my ( $val, $msg ) = $Ticket->MergeInto( $ARGSRef->{ $Ticket->Id . "-MergeInto" } );
- push @results, $msg;
- }
-
- return (@results);
-}
-
-# }}}
-
-sub ProcessRecordLinks {
- my %args = (
- RecordObj => undef,
- ARGSRef => undef,
- @_
- );
-
- my $Record = $args{'RecordObj'};
- my $ARGSRef = $args{'ARGSRef'};
-
my (@results);
# Delete links that are gone gone gone.
@@ -1877,11 +1323,11 @@ sub ProcessRecordLinks {
my $type = $2;
my $target = $3;
- my ( $val, $msg ) = $Record->DeleteLink(
- Base => $base,
- Type => $type,
- Target => $target
- );
+ push @results,
+ "Trying to delete: Base: $base Target: $target Type $type";
+ my ( $val, $msg ) = $Ticket->DeleteLink( Base => $base,
+ Type => $type,
+ Target => $target );
push @results, $msg;
@@ -1892,138 +1338,40 @@ sub ProcessRecordLinks {
my @linktypes = qw( DependsOn MemberOf RefersTo );
foreach my $linktype (@linktypes) {
- if ( $ARGSRef->{ $Record->Id . "-$linktype" } ) {
- $ARGSRef->{ $Record->Id . "-$linktype" } = join( ' ', @{ $ARGSRef->{ $Record->Id . "-$linktype" } } )
- if ref( $ARGSRef->{ $Record->Id . "-$linktype" } );
-
- for my $luri ( split( / /, $ARGSRef->{ $Record->Id . "-$linktype" } ) ) {
- next unless $luri;
- $luri =~ s/\s+$//; # Strip trailing whitespace
- my ( $val, $msg ) = $Record->AddLink(
- Target => $luri,
- Type => $linktype
- );
+ if ( $ARGSRef->{ $Ticket->Id . "-$linktype" } ) {
+ for my $luri ( split ( / /, $ARGSRef->{ $Ticket->Id . "-$linktype" } ) ) {
+ $luri =~ s/\s*$//; # Strip trailing whitespace
+ my ( $val, $msg ) = $Ticket->AddLink( Target => $luri,
+ Type => $linktype );
push @results, $msg;
}
}
- if ( $ARGSRef->{ "$linktype-" . $Record->Id } ) {
- $ARGSRef->{ "$linktype-" . $Record->Id } = join( ' ', @{ $ARGSRef->{ "$linktype-" . $Record->Id } } )
- if ref( $ARGSRef->{ "$linktype-" . $Record->Id } );
-
- for my $luri ( split( / /, $ARGSRef->{ "$linktype-" . $Record->Id } ) ) {
- next unless $luri;
- my ( $val, $msg ) = $Record->AddLink(
- Base => $luri,
- Type => $linktype
- );
+ if ( $ARGSRef->{ "$linktype-" . $Ticket->Id } ) {
+
+ for my $luri ( split ( / /, $ARGSRef->{ "$linktype-" . $Ticket->Id } ) ) {
+ my ( $val, $msg ) = $Ticket->AddLink( Base => $luri,
+ Type => $linktype );
push @results, $msg;
}
- }
+ }
}
- return (@results);
-}
-
-=head2 _UploadedFile ( $arg );
-
-Takes a CGI parameter name; if a file is uploaded under that name,
-return a hash reference suitable for AddCustomFieldValue's use:
-C<( Value => $filename, LargeContent => $content, ContentType => $type )>.
-
-Returns C<undef> if no files were uploaded in the C<$arg> field.
-
-=cut
-
-sub _UploadedFile {
- my $arg = shift;
- my $cgi_object = $m->cgi_object;
- my $fh = $cgi_object->upload($arg) or return undef;
- my $upload_info = $cgi_object->uploadInfo($fh);
-
- my $filename = "$fh";
- $filename =~ s#^.*[\\/]##;
- binmode($fh);
-
- return {
- Value => $filename,
- LargeContent => do { local $/; scalar <$fh> },
- ContentType => $upload_info->{'Content-Type'},
- };
-}
-
-sub GetColumnMapEntry {
- my %args = ( Map => {}, Name => '', Attribute => undef, @_ );
-
- # deal with the simplest thing first
- if ( $args{'Map'}{ $args{'Name'} } ) {
- return $args{'Map'}{ $args{'Name'} }{ $args{'Attribute'} };
- }
-
- # complex things
- elsif ( my ( $mainkey, $subkey ) = $args{'Name'} =~ /^(.*?)\.{(.+)}$/ ) {
- return undef unless $args{'Map'}->{$mainkey};
- return $args{'Map'}{$mainkey}{ $args{'Attribute'} }
- unless ref $args{'Map'}{$mainkey}{ $args{'Attribute'} } eq 'CODE';
-
- return sub { $args{'Map'}{$mainkey}{ $args{'Attribute'} }->( @_, $subkey ) };
- }
- return undef;
-}
-
-sub ProcessColumnMapValue {
- my $value = shift;
- my %args = ( Arguments => [], Escape => 1, @_ );
-
- if ( ref $value ) {
- if ( UNIVERSAL::isa( $value, 'CODE' ) ) {
- my @tmp = $value->( @{ $args{'Arguments'} } );
- return ProcessColumnMapValue( ( @tmp > 1 ? \@tmp : $tmp[0] ), %args );
- } elsif ( UNIVERSAL::isa( $value, 'ARRAY' ) ) {
- return join '', map ProcessColumnMapValue( $_, %args ), @$value;
- } elsif ( UNIVERSAL::isa( $value, 'SCALAR' ) ) {
- return $$value;
- }
+ #Merge if we need to
+ if ( $ARGSRef->{ $Ticket->Id . "-MergeInto" } ) {
+ my ( $val, $msg ) =
+ $Ticket->MergeInto( $ARGSRef->{ $Ticket->Id . "-MergeInto" } );
+ push @results, $msg;
}
- return $m->interp->apply_escapes( $value, 'h' ) if $args{'Escape'};
- return $value;
-}
-
-=head2 _load_container_object ( $type, $id );
-
-Instantiate container object for saving searches.
-
-=cut
-
-sub _load_container_object {
- my ( $obj_type, $obj_id ) = @_;
- return RT::SavedSearch->new( $session{'CurrentUser'} )->_load_privacy_object( $obj_type, $obj_id );
+ return (@results);
}
-=head2 _parse_saved_search ( $arg );
-
-Given a serialization string for saved search, and returns the
-container object and the search id.
-
-=cut
-
-sub _parse_saved_search {
- my $spec = shift;
- return unless $spec;
- if ( $spec !~ /^(.*?)-(\d+)-SavedSearch-(\d+)$/ ) {
- return;
- }
- my $obj_type = $1;
- my $obj_id = $2;
- my $search_id = $3;
-
- return ( _load_container_object( $obj_type, $obj_id ), $search_id );
-}
+# }}}
eval "require RT::Interface::Web_Vendor";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Interface/Web_Vendor.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web_Vendor.pm});
eval "require RT::Interface::Web_Local";
-die $@ if ( $@ && $@ !~ qr{^Can't locate RT/Interface/Web_Local.pm} );
+die $@ if ($@ && $@ !~ qr{^Can't locate RT/Interface/Web_Local.pm});
1;
diff --git a/rt/lib/RT/Interface/Web_Vendor.pm b/rt/lib/RT/Interface/Web_Vendor.pm
new file mode 100644
index 000000000..1999096a7
--- /dev/null
+++ b/rt/lib/RT/Interface/Web_Vendor.pm
@@ -0,0 +1,201 @@
+# Copyright (c) 2004 Ivan Kohler <ivan-rt@420.am>
+# Copyright (c) 2008 Freeside Internet Services, Inc.
+#
+# 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.
+
+=head1 NAME
+
+RT::Interface::Web_Vendor
+
+=head1 SYNOPSIS
+
+=head1 DESCRIPTION
+
+Freeside vendor overlay for RT::Interface::Web.
+
+=begin testing
+
+use_ok(RT::Interface::Web_Vendor);
+
+=end testing
+
+=cut
+
+#package RT::Interface::Web;
+#use strict;
+
+package HTML::Mason::Commands;
+use strict;
+
+=head2 ProcessTicketCustomers
+
+=cut
+
+sub ProcessTicketCustomers {
+ my %args = (
+ TicketObj => undef,
+ ARGSRef => undef,
+ Debug => 0,
+ @_
+ );
+ my @results = ();
+
+ my $Ticket = $args{'TicketObj'};
+ my $ARGSRef = $args{'ARGSRef'};
+ my $Debug = $args{'Debug'};
+ my $me = 'ProcessTicketCustomers';
+
+ ### false laziness w/RT::Interface::Web::ProcessTicketLinks
+ # Delete links that are gone gone gone.
+ foreach my $arg ( keys %$ARGSRef ) {
+ if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) {
+ my $base = $1;
+ my $type = $2;
+ my $target = $3;
+
+ push @results,
+ "Trying to delete: Base: $base Target: $target Type $type";
+ my ( $val, $msg ) = $Ticket->DeleteLink( Base => $base,
+ Type => $type,
+ Target => $target );
+
+ push @results, $msg;
+
+ }
+
+ }
+ ###
+
+ ###
+ #find new customers
+ ###
+
+ my @custnums = map { /^Ticket-AddCustomer-(\d+)$/; $1 }
+ grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ keys %$ARGSRef;
+
+ #my @delete_custnums =
+ # map { /^Ticket-AddCustomer-(\d+)$/; $1 }
+ # grep { /^Ticket-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ # keys %$ARGSRef;
+
+ ###
+ #figure out if we're going to auto-link requestors, and find them if so
+ ###
+
+ my $num_cur_cust = $Ticket->Customers->Count;
+ my $num_new_cust = scalar(@custnums);
+ warn "$me: $num_cur_cust current customers / $num_new_cust new customers\n"
+ if $Debug;
+
+ #if we're linking the first ticket to one customer
+ my $link_requestors = ( $num_cur_cust == 0 && $num_new_cust == 1 );
+ warn "$me: adding a single customer to a previously customerless".
+ " ticket, so linking customers to requestor too\n"
+ if $Debug && $link_requestors;
+
+ my @Requestors = ();
+ if ( $link_requestors ) {
+
+ #find any requestors without customers
+ @Requestors =
+ grep { ! $_->Customers->Count }
+ @{ $Ticket->Requestors->UserMembersObj->ItemsArrayRef };
+
+ warn "$me: found ". scalar(@Requestors). " requestors without".
+ " customers; linking them\n"
+ if $Debug;
+
+ }
+
+ ###
+ #link ticket (and requestors) to customers
+ ###
+
+ foreach my $custnum ( @custnums ) {
+
+ my @link = ( 'Type' => 'MemberOf',
+ 'Target' => "freeside://freeside/cust_main/$custnum",
+ );
+
+ my( $val, $msg ) = $Ticket->AddLink(@link);
+ push @results, $msg;
+
+ #add customer links to requestors
+ foreach my $Requestor ( @Requestors ) {
+ my( $val, $msg ) = $Requestor->AddLink(@link);
+ push @results, $msg;
+ warn "$me: linking requestor to custnum $custnum: $msg\n"
+ if $Debug > 1;
+ }
+
+ }
+
+ return @results;
+
+}
+
+#false laziness w/above... eventually it should go away in favor of this
+sub ProcessObjectCustomers {
+ my %args = (
+ Object => undef,
+ ARGSRef => undef,
+ @_
+ );
+ my @results = ();
+
+ my $Object = $args{'Object'};
+ my $ARGSRef = $args{'ARGSRef'};
+
+ ### false laziness w/RT::Interface::Web::ProcessTicketLinks
+ # Delete links that are gone gone gone.
+ foreach my $arg ( keys %$ARGSRef ) {
+ if ( $arg =~ /DeleteLink-(.*?)-(DependsOn|MemberOf|RefersTo)-(.*)$/ ) {
+ my $base = $1;
+ my $type = $2;
+ my $target = $3;
+
+ push @results,
+ "Trying to delete: Base: $base Target: $target Type $type";
+ my ( $val, $msg ) = $Object->DeleteLink( Base => $base,
+ Type => $type,
+ Target => $target );
+
+ push @results, $msg;
+
+ }
+
+ }
+ ###
+
+ #my @delete_custnums =
+ # map { /^Object-AddCustomer-(\d+)$/; $1 }
+ # grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ # keys %$ARGSRef;
+
+ my @custnums = map { /^Object-AddCustomer-(\d+)$/; $1 }
+ grep { /^Object-AddCustomer-(\d+)$/ && $ARGSRef->{$_} }
+ keys %$ARGSRef;
+
+ foreach my $custnum ( @custnums ) {
+ my( $val, $msg ) =
+ $Object->AddLink( 'Type' => 'MemberOf',
+ 'Target' => "freeside://freeside/cust_main/$custnum",
+ );
+ push @results, $msg;
+ }
+
+ return @results;
+
+}
+
+1;
+
diff --git a/rt/lib/RT/Link.pm b/rt/lib/RT/Link.pm
index 0cab23790..962c378a8 100644
--- a/rt/lib/RT/Link.pm
+++ b/rt/lib/RT/Link.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -123,7 +98,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -132,14 +107,14 @@ Returns the current value of id.
=cut
-=head2 Base
+=item Base
Returns the current value of Base.
(In the database, Base is stored as varchar(240).)
-=head2 SetBase VALUE
+=item SetBase VALUE
Set Base to VALUE.
@@ -150,14 +125,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Target
+=item Target
Returns the current value of Target.
(In the database, Target is stored as varchar(240).)
-=head2 SetTarget VALUE
+=item SetTarget VALUE
Set Target to VALUE.
@@ -168,14 +143,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Type
+=item Type
Returns the current value of Type.
(In the database, Type is stored as varchar(20).)
-=head2 SetType VALUE
+=item SetType VALUE
Set Type to VALUE.
@@ -186,14 +161,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 LocalTarget
+=item LocalTarget
Returns the current value of LocalTarget.
(In the database, LocalTarget is stored as int(11).)
-=head2 SetLocalTarget VALUE
+=item SetLocalTarget VALUE
Set LocalTarget to VALUE.
@@ -204,14 +179,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 LocalBase
+=item LocalBase
Returns the current value of LocalBase.
(In the database, LocalBase is stored as int(11).)
-=head2 SetLocalBase VALUE
+=item SetLocalBase VALUE
Set LocalBase to VALUE.
@@ -222,7 +197,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -231,7 +206,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -240,7 +215,7 @@ Returns the current value of LastUpdated.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -249,7 +224,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -259,29 +234,29 @@ Returns the current value of Created.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Base =>
- {read => 1, write => 1, sql_type => 12, length => 240, is_blob => 0, is_numeric => 0, type => 'varchar(240)', default => ''},
+ {read => 1, write => 1, type => 'varchar(240)', default => ''},
Target =>
- {read => 1, write => 1, sql_type => 12, length => 240, is_blob => 0, is_numeric => 0, type => 'varchar(240)', default => ''},
+ {read => 1, write => 1, type => 'varchar(240)', default => ''},
Type =>
- {read => 1, write => 1, sql_type => 12, length => 20, is_blob => 0, is_numeric => 0, type => 'varchar(20)', default => ''},
+ {read => 1, write => 1, type => 'varchar(20)', default => ''},
LocalTarget =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
LocalBase =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -313,7 +288,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Links.pm b/rt/lib/RT/Links.pm
index ecf9643fd..7a1773af9 100644
--- a/rt/lib/RT/Links.pm
+++ b/rt/lib/RT/Links.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Link item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Queue.pm b/rt/lib/RT/Queue.pm
index 183ab8f9f..b362c9f0d 100755
--- a/rt/lib/RT/Queue.pm
+++ b/rt/lib/RT/Queue.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -132,7 +107,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -141,14 +116,14 @@ Returns the current value of id.
=cut
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -159,14 +134,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -177,14 +152,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 CorrespondAddress
+=item CorrespondAddress
Returns the current value of CorrespondAddress.
(In the database, CorrespondAddress is stored as varchar(120).)
-=head2 SetCorrespondAddress VALUE
+=item SetCorrespondAddress VALUE
Set CorrespondAddress to VALUE.
@@ -195,14 +170,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 CommentAddress
+=item CommentAddress
Returns the current value of CommentAddress.
(In the database, CommentAddress is stored as varchar(120).)
-=head2 SetCommentAddress VALUE
+=item SetCommentAddress VALUE
Set CommentAddress to VALUE.
@@ -213,14 +188,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 InitialPriority
+=item InitialPriority
Returns the current value of InitialPriority.
(In the database, InitialPriority is stored as int(11).)
-=head2 SetInitialPriority VALUE
+=item SetInitialPriority VALUE
Set InitialPriority to VALUE.
@@ -231,14 +206,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 FinalPriority
+=item FinalPriority
Returns the current value of FinalPriority.
(In the database, FinalPriority is stored as int(11).)
-=head2 SetFinalPriority VALUE
+=item SetFinalPriority VALUE
Set FinalPriority to VALUE.
@@ -249,14 +224,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 DefaultDueIn
+=item DefaultDueIn
Returns the current value of DefaultDueIn.
(In the database, DefaultDueIn is stored as int(11).)
-=head2 SetDefaultDueIn VALUE
+=item SetDefaultDueIn VALUE
Set DefaultDueIn to VALUE.
@@ -267,7 +242,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -276,7 +251,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -285,7 +260,7 @@ Returns the current value of Created.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -294,7 +269,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -303,14 +278,14 @@ Returns the current value of LastUpdated.
=cut
-=head2 Disabled
+=item Disabled
Returns the current value of Disabled.
(In the database, Disabled is stored as smallint(6).)
-=head2 SetDisabled VALUE
+=item SetDisabled VALUE
Set Disabled to VALUE.
@@ -322,35 +297,35 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
CorrespondAddress =>
- {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ {read => 1, write => 1, type => 'varchar(120)', default => ''},
CommentAddress =>
- {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ {read => 1, write => 1, type => 'varchar(120)', default => ''},
InitialPriority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
FinalPriority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
DefaultDueIn =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
Disabled =>
- {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+ {read => 1, write => 1, type => 'smallint(6)', default => '0'},
}
};
@@ -382,7 +357,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Queues.pm b/rt/lib/RT/Queues.pm
index 48f40f1e5..60aec9086 100755
--- a/rt/lib/RT/Queues.pm
+++ b/rt/lib/RT/Queues.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Queue item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Record.pm b/rt/lib/RT/Record.pm
index b2e1e7110..8664e2867 100755
--- a/rt/lib/RT/Record.pm
+++ b/rt/lib/RT/Record.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::Record - Base class for RT record objects
@@ -56,6 +55,11 @@
=head1 DESCRIPTION
+=begin testing
+
+ok (require RT::Record);
+
+=end testing
=head1 METHODS
@@ -66,21 +70,25 @@ package RT::Record;
use strict;
use warnings;
+our @ISA;
+use base qw(RT::Base);
+
use RT::Date;
+use RT::I18N;
use RT::User;
use RT::Attributes;
+use DBIx::SearchBuilder::Record::Cachable;
use Encode qw();
our $_TABLE_ATTR = { };
-use RT::Base;
-my $base = 'DBIx::SearchBuilder::Record::Cachable';
-if ( $RT::Config && $RT::Config->Get('DontCacheSearchBuilderRecords') ) {
- $base = 'DBIx::SearchBuilder::Record';
+
+if ( $RT::DontCacheSearchBuilderRecords ) {
+ push (@ISA, 'DBIx::SearchBuilder::Record');
+} else {
+ push (@ISA, 'DBIx::SearchBuilder::Record::Cachable');
+
}
-eval "require $base" or die $@;
-our @ISA = 'RT::Base';
-push @ISA, $base;
# {{{ sub _Init
@@ -100,7 +108,10 @@ The primary keys for RT classes is 'id'
=cut
-sub _PrimaryKeys { return ['id'] }
+sub _PrimaryKeys {
+ my $self = shift;
+ return ( ['id'] );
+}
# }}}
@@ -126,6 +137,14 @@ sub Delete {
Returns a string which is this object's type. The type is the class,
without the "RT::" prefix.
+=begin testing
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my $group = RT::Group->new($RT::SystemUser);
+is($ticket->ObjectTypeStr, 'Ticket', "Ticket returns correct typestring");
+is($group->ObjectTypeStr, 'Group', "Group returns correct typestring");
+
+=end testing
=cut
@@ -241,7 +260,10 @@ sub FirstAttribute {
# {{{ sub _Handle
-sub _Handle { return $RT::Handle }
+sub _Handle {
+ my $self = shift;
+ return ($RT::Handle);
+}
# }}}
@@ -314,6 +336,8 @@ sub Create {
}
if (UNIVERSAL::isa('errno',$id)) {
+ exit(0);
+ warn "It's here!";
return(undef);
}
@@ -343,29 +367,40 @@ DB is case sensitive
sub LoadByCols {
my $self = shift;
+ my %hash = (@_);
# We don't want to hang onto this
delete $self->{'attributes'};
- return $self->SUPER::LoadByCols( @_ ) unless $self->_Handle->CaseSensitive;
-
# If this database is case sensitive we need to uncase objects for
# explicit loading
- my %hash = (@_);
- foreach my $key ( keys %hash ) {
-
- # If we've been passed an empty value, we can't do the lookup.
- # We don't need to explicitly downcase integers or an id.
- if ( $key ne 'id' && defined $hash{ $key } && $hash{ $key } !~ /^\d+$/ ) {
- my ($op, $val, $func);
- ($key, $op, $val, $func) =
- $self->_Handle->_MakeClauseCaseInsensitive( $key, '=', delete $hash{ $key } );
- $hash{$key}->{operator} = $op;
- $hash{$key}->{value} = $val;
- $hash{$key}->{function} = $func;
+ if ( $self->_Handle->CaseSensitive ) {
+ my %newhash;
+ foreach my $key ( keys %hash ) {
+
+ # If we've been passed an empty value, we can't do the lookup.
+ # We don't need to explicitly downcase integers or an id.
+ if ( $key =~ '^id$'
+ || !defined( $hash{$key} )
+ || $hash{$key} =~ /^\d+$/
+ )
+ {
+ $newhash{$key} = $hash{$key};
+ }
+ else {
+ my ($op, $val, $func);
+ ($key, $op, $val, $func) = $self->_Handle->_MakeClauseCaseInsensitive($key, '=', $hash{$key});
+ $newhash{$key}->{operator} = $op;
+ $newhash{$key}->{value} = $val;
+ $newhash{$key}->{function} = $func;
+ }
}
+
+ # We've clobbered everything we care about. bash the old hash
+ # and replace it with the new hash
+ %hash = %newhash;
}
- return $self->SUPER::LoadByCols( %hash );
+ $self->SUPER::LoadByCols(%hash);
}
# }}}
@@ -494,7 +529,7 @@ sub _Set {
$msg =
$self->loc(
"[_1] changed from [_2] to [_3]",
- $self->loc( $args{'Field'} ),
+ $args{'Field'},
( $old_val ? "'$old_val'" : $self->loc("(no value)") ),
'"' . $self->__Value( $args{'Field'}) . '"'
);
@@ -627,20 +662,27 @@ sub SQLType {
}
+
sub __Value {
my $self = shift;
my $field = shift;
- my %args = ( decode_utf8 => 1, @_ );
+ my %args = ( decode_utf8 => 1,
+ @_ );
- unless ( $field ) {
- $RT::Logger->error("__Value called with undef field");
+ unless (defined $field && $field) {
+ $RT::Logger->error("$self __Value called with undef field");
}
+ my $value = $self->SUPER::__Value($field);
+
+ return('') if ( !defined($value) || $value eq '');
- my $value = $self->SUPER::__Value( $field );
if( $args{'decode_utf8'} ) {
- return Encode::decode_utf8( $value ) unless Encode::is_utf8( $value );
+ # XXX: is_utf8 check should be here unless Encode bug would be fixed
+ # see http://rt.cpan.org/NoAuth/Bug.html?id=14559
+ return Encode::decode_utf8($value) unless Encode::is_utf8($value);
} else {
- return Encode::encode_utf8( $value ) if Encode::is_utf8( $value );
+ # check is_utf8 here just to be shure
+ return Encode::encode_utf8($value) if Encode::is_utf8($value);
}
return $value;
}
@@ -658,7 +700,6 @@ sub _CacheConfig {
sub _BuildTableAttributes {
my $self = shift;
- my $class = ref($self) || $self;
my $attributes;
if ( UNIVERSAL::can( $self, '_CoreAccessible' ) ) {
@@ -670,19 +711,37 @@ sub _BuildTableAttributes {
foreach my $column (%$attributes) {
foreach my $attr ( %{ $attributes->{$column} } ) {
- $_TABLE_ATTR->{$class}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ }
+ }
+ if ( UNIVERSAL::can( $self, '_OverlayAccessible' ) ) {
+ $attributes = $self->_OverlayAccessible();
+
+ foreach my $column (%$attributes) {
+ foreach my $attr ( %{ $attributes->{$column} } ) {
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ }
}
}
- foreach my $method ( qw(_OverlayAccessible _VendorAccessible _LocalAccessible) ) {
- next unless UNIVERSAL::can( $self, $method );
- $attributes = $self->$method();
+ if ( UNIVERSAL::can( $self, '_VendorAccessible' ) ) {
+ $attributes = $self->_VendorAccessible();
foreach my $column (%$attributes) {
foreach my $attr ( %{ $attributes->{$column} } ) {
- $_TABLE_ATTR->{$class}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
}
}
}
+ if ( UNIVERSAL::can( $self, '_LocalAccessible' ) ) {
+ $attributes = $self->_LocalAccessible();
+
+ foreach my $column (%$attributes) {
+ foreach my $attr ( %{ $attributes->{$column} } ) {
+ $_TABLE_ATTR->{ref($self)}->{$column}->{$attr} = $attributes->{$column}->{$attr};
+ }
+ }
+ }
+
}
@@ -695,7 +754,7 @@ DBIx::SearchBuilder::Record
sub _ClassAccessible {
my $self = shift;
- return $_TABLE_ATTR->{ref($self) || $self};
+ return $_TABLE_ATTR->{ref($self)};
}
=head2 _Accessible COLUMN ATTRIBUTE
@@ -728,19 +787,19 @@ sub _EncodeLOB {
my $ContentEncoding = 'none';
#get the max attachment length from RT
- my $MaxSize = RT->Config->Get('MaxAttachmentSize');
+ my $MaxSize = $RT::MaxAttachmentSize;
#if the current attachment contains nulls and the
#database doesn't support embedded nulls
- if ( RT->Config->Get('AlwaysUseBase64') or
+ if ( $RT::AlwaysUseBase64 or
( !$RT::Handle->BinarySafeBLOBs ) && ( $Body =~ /\x00/ ) ) {
# set a flag telling us to mimencode the attachment
$ContentEncoding = 'base64';
#cut the max attchment size by 25% (for mime-encoding overhead.
- $RT::Logger->debug("Max size is $MaxSize");
+ $RT::Logger->debug("Max size is $MaxSize\n");
$MaxSize = $MaxSize * 3 / 4;
# Some databases (postgres) can't handle non-utf8 data
} elsif ( !$RT::Handle->BinarySafeBLOBs
@@ -753,7 +812,7 @@ sub _EncodeLOB {
if ( ($MaxSize) and ( $MaxSize < length($Body) ) ) {
# if we're supposed to truncate large attachments
- if (RT->Config->Get('TruncateLongAttachments')) {
+ if ($RT::TruncateLongAttachments) {
# truncate the attachment to that length.
$Body = substr( $Body, 0, $MaxSize );
@@ -761,12 +820,13 @@ sub _EncodeLOB {
}
# elsif we're supposed to drop large attachments on the floor,
- elsif (RT->Config->Get('DropLongAttachments')) {
+ elsif ($RT::DropLongAttachments) {
# drop the attachment on the floor
$RT::Logger->info( "$self: Dropped an attachment of size "
- . length($Body));
- $RT::Logger->info( "It started: " . substr( $Body, 0, 60 ) );
+ . length($Body) . "\n"
+ . "It started: " . substr( $Body, 0, 60 ) . "\n"
+ );
return ("none", "Large attachment dropped" );
}
}
@@ -790,8 +850,8 @@ sub _EncodeLOB {
sub _DecodeLOB {
my $self = shift;
- my $ContentType = shift || '';
- my $ContentEncoding = shift || 'none';
+ my $ContentType = shift;
+ my $ContentEncoding = shift;
my $Content = shift;
if ( $ContentEncoding eq 'base64' ) {
@@ -803,12 +863,14 @@ sub _DecodeLOB {
elsif ( $ContentEncoding && $ContentEncoding ne 'none' ) {
return ( $self->loc( "Unknown ContentEncoding [_1]", $ContentEncoding ) );
}
+
if ( RT::I18N::IsTextualContentType($ContentType) ) {
$Content = Encode::decode_utf8($Content) unless Encode::is_utf8($Content);
}
return ($Content);
}
+# {{{ LINKDIRMAP
# A helper table for links mapping to make it easier
# to build and parse links between tickets
@@ -882,18 +944,11 @@ sub Update {
# This is in an eval block because $object might not exist.
# and might not have a Name method. But "can" won't find autoloaded
# items. If it fails, we don't care
- do {
- no warnings "uninitialized";
- local $@;
- eval {
- my $object = $attribute . "Obj";
- my $name = $self->$object->Name;
- next if $name eq $value || $name eq ($value || 0);
- };
- next if $value eq $self->$attribute();
- next if ($value || 0) eq $self->$attribute();
+ eval {
+ my $object = $attribute . "Obj";
+ next if ($self->$object->Name eq $value);
};
-
+ next if ( $value eq $self->$attribute() );
my $method = "Set$attribute";
my ( $code, $msg ) = $self->$method($value);
my ($prefix) = ref($self) =~ /RT(?:.*)::(\w+)/;
@@ -901,17 +956,7 @@ sub Update {
# Default to $id, but use name if we can get it.
my $label = $self->id;
$label = $self->Name if (UNIVERSAL::can($self,'Name'));
- # this requires model names to be loc'ed.
-
-=for loc
-
- "Ticket" # loc
- "User" # loc
- "Group" # loc
- "Queue" # loc
-=cut
-
- push @results, $self->loc( $prefix ) . " $label: ". $msg;
+ push @results, $self->loc( "$prefix [_1]", $label ) . ': '. $msg;
=for loc
@@ -1020,10 +1065,54 @@ sub DependedOnBy {
=head2 HasUnresolvedDependencies
-Takes a paramhash of Type (default to '__any'). Returns the number of
-unresolved dependencies, if $self->UnresolvedDependencies returns an
-object with one or more members of that type. Returns false
-otherwise.
+ Takes a paramhash of Type (default to '__any'). Returns true if
+$self->UnresolvedDependencies returns an object with one or more members
+of that type. Returns false otherwise
+
+
+=begin testing
+
+my $t1 = RT::Ticket->new($RT::SystemUser);
+my ($id, $trans, $msg) = $t1->Create(Subject => 'DepTest1', Queue => 'general');
+ok($id, "Created dep test 1 - $msg");
+
+my $t2 = RT::Ticket->new($RT::SystemUser);
+my ($id2, $trans, $msg2) = $t2->Create(Subject => 'DepTest2', Queue => 'general');
+ok($id2, "Created dep test 2 - $msg2");
+my $t3 = RT::Ticket->new($RT::SystemUser);
+my ($id3, $trans, $msg3) = $t3->Create(Subject => 'DepTest3', Queue => 'general', Type => 'approval');
+ok($id3, "Created dep test 3 - $msg3");
+my ($addid, $addmsg);
+ok (($addid, $addmsg) =$t1->AddLink( Type => 'DependsOn', Target => $t2->id));
+ok ($addid, $addmsg);
+ok (($addid, $addmsg) =$t1->AddLink( Type => 'DependsOn', Target => $t3->id));
+
+ok ($addid, $addmsg);
+my $link = RT::Link->new($RT::SystemUser);
+my ($rv, $msg) = $link->Load($addid);
+ok ($rv, $msg);
+ok ($link->LocalTarget == $t3->id, "Link LocalTarget is correct");
+ok ($link->LocalBase == $t1->id, "Link LocalBase is correct");
+
+ok ($t1->HasUnresolvedDependencies, "Ticket ".$t1->Id." has unresolved deps");
+ok (!$t1->HasUnresolvedDependencies( Type => 'blah' ), "Ticket ".$t1->Id." has no unresolved blahs");
+ok ($t1->HasUnresolvedDependencies( Type => 'approval' ), "Ticket ".$t1->Id." has unresolved approvals");
+ok (!$t2->HasUnresolvedDependencies, "Ticket ".$t2->Id." has no unresolved deps");
+;
+
+my ($rid, $rmsg)= $t1->Resolve();
+ok(!$rid, $rmsg);
+my ($rid2, $rmsg2) = $t2->Resolve();
+ok ($rid2, $rmsg2);
+($rid, $rmsg)= $t1->Resolve();
+ok(!$rid, $rmsg);
+my ($rid3,$rmsg3) = $t3->Resolve;
+ok ($rid3,$rmsg3);
+($rid, $rmsg)= $t1->Resolve();
+ok($rid, $rmsg);
+
+
+=end testing
=cut
@@ -1046,7 +1135,7 @@ sub HasUnresolvedDependencies {
}
if ($deps->Count > 0) {
- return $deps->Count;
+ return 1;
}
else {
return (undef);
@@ -1095,54 +1184,27 @@ dependency search.
sub AllDependedOnBy {
my $self = shift;
- return $self->_AllLinkedTickets( LinkType => 'DependsOn',
- Direction => 'Target', @_ );
-}
-
-=head2 AllDependsOn
-
-Returns an array of RT::Ticket objects which this ticket (directly or
-indirectly) depends on; takes an optional 'Type' argument in the param
-hash, which will limit returned tickets to that type, as well as cause
-tickets with that type to serve as 'leaf' nodes that stops the
-recursive dependency search.
-
-=cut
-
-sub AllDependsOn {
- my $self = shift;
- return $self->_AllLinkedTickets( LinkType => 'DependsOn',
- Direction => 'Base', @_ );
-}
-
-sub _AllLinkedTickets {
- my $self = shift;
-
+ my $dep = $self->DependedOnBy;
my %args = (
- LinkType => undef,
- Direction => undef,
Type => undef,
_found => {},
_top => 1,
@_
);
- my $dep = $self->_Links( $args{Direction}, $args{LinkType});
while (my $link = $dep->Next()) {
- my $uri = $args{Direction} eq 'Target' ? $link->BaseURI : $link->TargetURI;
- next unless ($uri->IsLocal());
- my $obj = $args{Direction} eq 'Target' ? $link->BaseObj : $link->TargetObj;
- next if $args{_found}{$obj->Id};
+ next unless ($link->BaseURI->IsLocal());
+ next if $args{_found}{$link->BaseObj->Id};
if (!$args{Type}) {
- $args{_found}{$obj->Id} = $obj;
- $obj->_AllLinkedTickets( %args, _top => 0 );
+ $args{_found}{$link->BaseObj->Id} = $link->BaseObj;
+ $link->BaseObj->AllDependedOnBy( %args, _top => 0 );
}
- elsif ($obj->Type eq $args{Type}) {
- $args{_found}{$obj->Id} = $obj;
+ elsif ($link->BaseObj->Type eq $args{Type}) {
+ $args{_found}{$link->BaseObj->Id} = $link->BaseObj;
}
else {
- $obj->_AllLinkedTickets( %args, _top => 0 );
+ $link->BaseObj->AllDependedOnBy( %args, _top => 0 );
}
}
@@ -1171,8 +1233,37 @@ sub DependsOn {
# }}}
+# {{{ Customers
+
+=head2 Customers
+
+ This returns an RT::Links object which references all the customers that this object is a member of.
+
+=cut
+sub Customers {
+ my( $self, %opt ) = @_;
+ my $Debug = $opt{'Debug'};
+
+ unless ( $self->{'Customers'} ) {
+
+ $self->{'Customers'} = $self->MemberOf->Clone;
+
+ $self->{'Customers'}->Limit(
+ FIELD => 'Target',
+ OPERATOR => 'STARTSWITH',
+ VALUE => 'freeside://freeside/cust_main/',
+ );
+ }
+ warn "->Customers method called on $self; returning ".
+ ref($self->{'Customers'}). ' object'
+ if $Debug;
+
+ return $self->{'Customers'};
+}
+
+# }}}
# {{{ sub _Links
@@ -1214,50 +1305,6 @@ sub _Links {
# }}}
-# {{{ sub FormatType
-
-=head2 FormatType
-
-Takes a Type and returns a string that is more human readable.
-
-=cut
-
-sub FormatType{
- my $self = shift;
- my %args = ( Type => '',
- @_
- );
- $args{Type} =~ s/([A-Z])/" " . lc $1/ge;
- $args{Type} =~ s/^\s+//;
- return $args{Type};
-}
-
-
-# }}}
-
-# {{{ sub FormatLink
-
-=head2 FormatLink
-
-Takes either a Target or a Base and returns a string of human friendly text.
-
-=cut
-
-sub FormatLink {
- my $self = shift;
- my %args = ( Object => undef,
- FallBack => '',
- @_
- );
- my $text = "URI " . $args{FallBack};
- if ($args{Object} && $args{Object}->isa("RT::Ticket")) {
- $text = "Ticket " . $args{Object}->id;
- }
- return $text;
-}
-
-# }}}
-
# {{{ sub _AddLink
=head2 _AddLink
@@ -1269,6 +1316,7 @@ Returns C<link id>, C<message> and C<exist> flag.
=cut
+
sub _AddLink {
my $self = shift;
my %args = ( Target => '',
@@ -1283,7 +1331,7 @@ sub _AddLink {
my $direction;
if ( $args{'Base'} and $args{'Target'} ) {
- $RT::Logger->debug( "$self tried to create a link. both base and target were specified" );
+ $RT::Logger->debug( "$self tried to create a link. both base and target were specified\n" );
return ( 0, $self->loc("Can't specifiy both base and target") );
}
elsif ( $args{'Base'} ) {
@@ -1325,14 +1373,10 @@ sub _AddLink {
return ( 0, $self->loc("Link could not be created") );
}
- my $basetext = $self->FormatLink(Object => $link->BaseObj,
- FallBack => $args{Base});
- my $targettext = $self->FormatLink(Object => $link->TargetObj,
- FallBack => $args{Target});
- my $typetext = $self->FormatType(Type => $args{Type});
my $TransString =
- "$basetext $typetext $targettext.";
- return ( $linkid, $TransString ) ;
+ "Record $args{'Base'} $args{Type} record $args{'Target'}.";
+
+ return ( $linkid, $self->loc( "Link created ([_1])", $TransString ) );
}
# }}}
@@ -1363,7 +1407,7 @@ sub _DeleteLink {
my $remote_link;
if ( $args{'Base'} and $args{'Target'} ) {
- $RT::Logger->debug("$self ->_DeleteLink. got both Base and Target");
+ $RT::Logger->debug("$self ->_DeleteLink. got both Base and Target\n");
return ( 0, $self->loc("Can't specifiy both base and target") );
}
elsif ( $args{'Base'} ) {
@@ -1377,32 +1421,28 @@ sub _DeleteLink {
$direction='Base';
}
else {
- $RT::Logger->error("Base or Target must be specified");
+ $RT::Logger->error("Base or Target must be specified\n");
return ( 0, $self->loc('Either base or target must be specified') );
}
my $link = new RT::Link( $self->CurrentUser );
- $RT::Logger->debug( "Trying to load link: " . $args{'Base'} . " " . $args{'Type'} . " " . $args{'Target'} );
+ $RT::Logger->debug( "Trying to load link: " . $args{'Base'} . " " . $args{'Type'} . " " . $args{'Target'} . "\n" );
$link->LoadByParams( Base=> $args{'Base'}, Type=> $args{'Type'}, Target=> $args{'Target'} );
#it's a real link.
-
if ( $link->id ) {
- my $basetext = $self->FormatLink(Object => $link->BaseObj,
- FallBack => $args{Base});
- my $targettext = $self->FormatLink(Object => $link->TargetObj,
- FallBack => $args{Target});
- my $typetext = $self->FormatType(Type => $args{Type});
+
my $linkid = $link->id;
$link->Delete();
- my $TransString = "$basetext no longer $typetext $targettext.";
- return ( 1, $TransString);
+
+ my $TransString = "Record $args{'Base'} no longer $args{Type} record $args{'Target'}.";
+ return ( 1, $self->loc("Link deleted ([_1])", $TransString));
}
#if it's not a link we can find
else {
- $RT::Logger->debug("Couldn't find that link");
+ $RT::Logger->debug("Couldn't find that link\n");
return ( 0, $self->loc("Link not found") );
}
}
@@ -1481,7 +1521,7 @@ sub _NewTransaction {
if ( defined $args{'TimeTaken'} and $self->can('_UpdateTimeTaken')) {
$self->_UpdateTimeTaken( $args{'TimeTaken'} );
}
- if ( RT->Config->Get('UseTransactionBatch') and $transaction ) {
+ if ( $RT::UseTransactionBatch and $transaction ) {
push @{$self->{_TransactionBatch}}, $trans if $args{'CommitScrips'};
}
return ( $transaction, $msg, $trans );
@@ -1524,13 +1564,11 @@ sub Transactions {
sub CustomFields {
my $self = shift;
my $cfs = RT::CustomFields->new( $self->CurrentUser );
-
- $cfs->SetContextObject( $self );
+
# XXX handle multiple types properly
$cfs->LimitToLookupType( $self->CustomFieldLookupType );
$cfs->LimitToGlobalOrObjectId(
- $self->_LookupId( $self->CustomFieldLookupType )
- );
+ $self->_LookupId( $self->CustomFieldLookupType ) );
return $cfs;
}
@@ -1563,18 +1601,27 @@ sub CustomFieldLookupType {
return ref($self);
}
+#TODO Deprecated API. Destroy in 3.6
+sub _LookupTypes {
+ my $self = shift;
+ $RT::Logger->warning("_LookupTypes call is deprecated at (". join(":",caller)."). Replace with CustomFieldLookupType");
+
+ return($self->CustomFieldLookupType);
+
+}
+
# {{{ AddCustomFieldValue
=head2 AddCustomFieldValue { Field => FIELD, Value => VALUE }
-VALUE should be a string. FIELD can be any identifier of a CustomField
-supported by L</LoadCustomFieldByIdentifier> method.
+VALUE should be a string.
+FIELD can be a CustomField object OR a CustomField ID.
-Adds VALUE as a value of CustomField FIELD. If this is a single-value custom field,
-deletes the old value.
+
+Adds VALUE as a value of CustomField FIELD. If this is a single-value custom field,
+deletes the old value.
If VALUE is not a valid value for the custom field, returns
-(0, 'Error message' ) otherwise, returns ($id, 'Success Message') where
-$id is ID of created L<ObjectCustomFieldValue> object.
+(0, 'Error message' ) otherwise, returns (1, 'Success Message')
=cut
@@ -1588,13 +1635,12 @@ sub _AddCustomFieldValue {
my %args = (
Field => undef,
Value => undef,
- LargeContent => undef,
- ContentType => undef,
RecordTransaction => 1,
@_
);
my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'});
+
unless ( $cf->Id ) {
return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) );
}
@@ -1610,11 +1656,8 @@ sub _AddCustomFieldValue {
)
);
}
-
- # empty string is not correct value of any CF, so undef it
- foreach ( qw(Value LargeContent) ) {
- $args{ $_ } = undef if defined $args{ $_ } && !length $args{ $_ };
- }
+ # Load up a ObjectCustomFieldValues object for this custom field and this ticket
+ my $values = $cf->ValuesForObject($self);
unless ( $cf->ValidateValue( $args{'Value'} ) ) {
return ( 0, $self->loc("Invalid value for custom field") );
@@ -1622,14 +1665,11 @@ sub _AddCustomFieldValue {
# If the custom field only accepts a certain # of values, delete the existing
# value and record a "changed from foo to bar" transaction
- unless ( $cf->UnlimitedValues ) {
-
- # Load up a ObjectCustomFieldValues object for this custom field and this ticket
- my $values = $cf->ValuesForObject($self);
+ unless ( $cf->UnlimitedValues) {
- # We need to whack any old values here. In most cases, the custom field should
- # only have one value to delete. In the pathalogical case, this custom field
- # used to be a multiple and we have many values to whack....
+ # We need to whack any old values here. In most cases, the custom field should
+ # only have one value to delete. In the pathalogical case, this custom field
+ # used to be a multiple and we have many values to whack....
my $cf_values = $values->Count;
if ( $cf_values > $cf->MaxValues ) {
@@ -1658,27 +1698,8 @@ sub _AddCustomFieldValue {
my ( $old_value, $old_content );
if ( $old_value = $values->First ) {
- $old_content = $old_value->Content;
- $old_content = undef if defined $old_content && !length $old_content;
-
- my $is_the_same = 1;
- if ( defined $args{'Value'} ) {
- $is_the_same = 0 unless defined $old_content
- && lc $old_content eq lc $args{'Value'};
- } else {
- $is_the_same = 0 if defined $old_content;
- }
- if ( $is_the_same ) {
- my $old_content = $old_value->LargeContent;
- if ( defined $args{'LargeContent'} ) {
- $is_the_same = 0 unless defined $old_content
- && $old_content eq $args{'LargeContent'};
- } else {
- $is_the_same = 0 if defined $old_content;
- }
- }
-
- return $old_value->id if $is_the_same;
+ $old_content = $old_value->Content();
+ return (1) if( $old_content eq $args{'Value'} && $old_value->LargeContent eq $args{'LargeContent'});;
}
my ( $new_value_id, $value_msg ) = $cf->AddValueForObject(
@@ -1688,17 +1709,19 @@ sub _AddCustomFieldValue {
ContentType => $args{'ContentType'},
);
- unless ( $new_value_id ) {
- return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg ) );
+ unless ($new_value_id) {
+ return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg) );
}
my $new_value = RT::ObjectCustomFieldValue->new( $self->CurrentUser );
- $new_value->Load( $new_value_id );
+ $new_value->Load($new_value_id);
# now that adding the new value was successful, delete the old one
- if ( $old_value ) {
+ if ($old_value) {
my ( $val, $msg ) = $old_value->Delete();
- return ( 0, $msg ) unless $val;
+ unless ($val) {
+ return ( 0, $msg );
+ }
}
if ( $args{'RecordTransaction'} ) {
@@ -1711,45 +1734,47 @@ sub _AddCustomFieldValue {
);
}
- my $new_content = $new_value->Content;
- unless ( defined $old_content && length $old_content ) {
- return ( $new_value_id, $self->loc( "[_1] [_2] added", $cf->Name, $new_content ));
+ if ( $old_value eq '' ) {
+ return ( 1, $self->loc( "[_1] [_2] added", $cf->Name, $new_value->Content ));
}
- elsif ( !defined $new_content || !length $new_content ) {
- return ( $new_value_id,
- $self->loc( "[_1] [_2] deleted", $cf->Name, $old_content ) );
+ elsif ( $new_value->Content eq '' ) {
+ return ( 1,
+ $self->loc( "[_1] [_2] deleted", $cf->Name, $old_value->Content ) );
}
else {
- return ( $new_value_id, $self->loc( "[_1] [_2] changed to [_3]", $cf->Name, $old_content, $new_content));
+ return ( 1, $self->loc( "[_1] [_2] changed to [_3]", $cf->Name, $old_content, $new_value->Content));
}
}
# otherwise, just add a new value and record "new value added"
else {
- my ($new_value_id, $msg) = $cf->AddValueForObject(
+ my ($new_value_id, $value_msg) = $cf->AddValueForObject(
Object => $self,
Content => $args{'Value'},
LargeContent => $args{'LargeContent'},
ContentType => $args{'ContentType'},
);
- unless ( $new_value_id ) {
- return ( 0, $self->loc( "Could not add new custom field value: [_1]", $msg ) );
+ unless ($new_value_id) {
+ return ( 0, $self->loc( "Could not add new custom field value: [_1]", $value_msg) );
}
if ( $args{'RecordTransaction'} ) {
- my ( $tid, $msg ) = $self->_NewTransaction(
+ my ( $TransactionId, $Msg, $TransactionObj ) =
+ $self->_NewTransaction(
Type => 'CustomField',
Field => $cf->Id,
NewReference => $new_value_id,
ReferenceType => 'RT::ObjectCustomFieldValue',
- );
- unless ( $tid ) {
- return ( 0, $self->loc( "Couldn't create a transaction: [_1]", $msg ) );
+ );
+ unless ($TransactionId) {
+ return ( 0,
+ $self->loc( "Couldn't create a transaction: [_1]", $Msg ) );
}
}
- return ( $new_value_id, $self->loc( "[_1] added as a value for [_2]", $args{'Value'}, $cf->Name ) );
+ return ( 1, $self->loc( "[_1] added as a value for [_2]", $args{'Value'}, $cf->Name));
}
+
}
# }}}
@@ -1777,10 +1802,10 @@ sub DeleteCustomFieldValue {
);
my $cf = $self->LoadCustomFieldByIdentifier($args{'Field'});
+
unless ( $cf->Id ) {
return ( 0, $self->loc( "Custom field [_1] not found", $args{'Field'} ) );
}
-
my ( $val, $msg ) = $cf->DeleteValueForObject(
Object => $self,
Id => $args{'ValueId'},
@@ -1789,7 +1814,6 @@ sub DeleteCustomFieldValue {
unless ($val) {
return ( 0, $msg );
}
-
my ( $TransactionId, $Msg, $TransactionObj ) = $self->_NewTransaction(
Type => 'CustomField',
Field => $cf->Id,
@@ -1823,34 +1847,15 @@ Takes a field id or name
sub FirstCustomFieldValue {
my $self = shift;
my $field = shift;
+ my $values = $self->CustomFieldValues($field);
+ if ($values->First) {
+ return $values->First->Content;
+ } else {
+ return undef;
+ }
- my $values = $self->CustomFieldValues( $field );
- return undef unless my $first = $values->First;
- return $first->Content;
}
-=head2 CustomFieldValuesAsString FIELD
-
-Return the content of the CustomField FIELD for this ticket.
-If this is a multi-value custom field, values will be joined with newlines.
-
-Takes a field id or name as the first argument
-
-Takes an optional Separator => "," second and third argument
-if you want to join the values using something other than a newline
-
-=cut
-
-sub CustomFieldValuesAsString {
- my $self = shift;
- my $field = shift;
- my %args = @_;
- my $separator = $args{Separator} || "\n";
-
- my $values = $self->CustomFieldValues( $field );
- return join ($separator, grep { defined $_ }
- map { $_->Content } @{$values->ItemsArrayRef});
-}
# {{{ CustomFieldValues
@@ -1868,12 +1873,11 @@ sub CustomFieldValues {
my $self = shift;
my $field = shift;
- if ( $field ) {
- my $cf = $self->LoadCustomFieldByIdentifier( $field );
+ if ($field) {
+ my $cf = $self->LoadCustomFieldByIdentifier($field);
- # we were asked to search on a custom field we couldn't find
+ # we were asked to search on a custom field we couldn't fine
unless ( $cf->id ) {
- $RT::Logger->warning("Couldn't load custom field by '$field' identifier");
return RT::ObjectCustomFieldValues->new( $self->CurrentUser );
}
return ( $cf->ValuesForObject($self) );
@@ -1881,11 +1885,12 @@ sub CustomFieldValues {
# we're not limiting to a specific custom field;
my $ocfs = RT::ObjectCustomFieldValues->new( $self->CurrentUser );
- $ocfs->LimitToObject( $self );
+ $ocfs->LimitToObject($self);
return $ocfs;
+
}
-=head2 LoadCustomFieldByIdentifier IDENTIFER
+=head2 CustomField IDENTIFER
Find the custom field has id or name IDENTIFIER for this object.
@@ -1897,32 +1902,35 @@ sub LoadCustomFieldByIdentifier {
my $self = shift;
my $field = shift;
- my $cf;
+ my $cf = RT::CustomField->new($self->CurrentUser);
+
if ( UNIVERSAL::isa( $field, "RT::CustomField" ) ) {
- $cf = RT::CustomField->new($self->CurrentUser);
- $cf->SetContextObject( $self );
$cf->LoadById( $field->id );
}
elsif ($field =~ /^\d+$/) {
$cf = RT::CustomField->new($self->CurrentUser);
- $cf->SetContextObject( $self );
- $cf->LoadById($field);
+ $cf->Load($field);
} else {
my $cfs = $self->CustomFields($self->CurrentUser);
- $cfs->SetContextObject( $self );
$cfs->Limit(FIELD => 'Name', VALUE => $field, CASESENSITIVE => 0);
$cf = $cfs->First || RT::CustomField->new($self->CurrentUser);
}
return $cf;
}
-sub ACLEquivalenceObjects { }
-sub BasicColumns { }
+# }}}
+
+# }}}
+
+# }}}
+
+sub BasicColumns {
+}
sub WikiBase {
- return RT->Config->Get('WebPath'). "/index.html?q=";
+ return $RT::WebPath. "/index.html?q=";
}
eval "require RT::Record_Vendor";
diff --git a/rt/lib/RT/Scrip.pm b/rt/lib/RT/Scrip.pm
index ecc47e923..a69dde04e 100755
--- a/rt/lib/RT/Scrip.pm
+++ b/rt/lib/RT/Scrip.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -90,7 +65,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -145,7 +120,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -154,14 +129,14 @@ Returns the current value of id.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -172,14 +147,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ScripCondition
+=item ScripCondition
Returns the current value of ScripCondition.
(In the database, ScripCondition is stored as int(11).)
-=head2 SetScripCondition VALUE
+=item SetScripCondition VALUE
Set ScripCondition to VALUE.
@@ -190,7 +165,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ScripConditionObj
+=item ScripConditionObj
Returns the ScripCondition Object which has the id returned by ScripCondition
@@ -204,14 +179,14 @@ sub ScripConditionObj {
return($ScripCondition);
}
-=head2 ScripAction
+=item ScripAction
Returns the current value of ScripAction.
(In the database, ScripAction is stored as int(11).)
-=head2 SetScripAction VALUE
+=item SetScripAction VALUE
Set ScripAction to VALUE.
@@ -222,7 +197,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ScripActionObj
+=item ScripActionObj
Returns the ScripAction Object which has the id returned by ScripAction
@@ -236,14 +211,14 @@ sub ScripActionObj {
return($ScripAction);
}
-=head2 ConditionRules
+=item ConditionRules
Returns the current value of ConditionRules.
(In the database, ConditionRules is stored as text.)
-=head2 SetConditionRules VALUE
+=item SetConditionRules VALUE
Set ConditionRules to VALUE.
@@ -254,14 +229,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ActionRules
+=item ActionRules
Returns the current value of ActionRules.
(In the database, ActionRules is stored as text.)
-=head2 SetActionRules VALUE
+=item SetActionRules VALUE
Set ActionRules to VALUE.
@@ -272,14 +247,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 CustomIsApplicableCode
+=item CustomIsApplicableCode
Returns the current value of CustomIsApplicableCode.
(In the database, CustomIsApplicableCode is stored as text.)
-=head2 SetCustomIsApplicableCode VALUE
+=item SetCustomIsApplicableCode VALUE
Set CustomIsApplicableCode to VALUE.
@@ -290,14 +265,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 CustomPrepareCode
+=item CustomPrepareCode
Returns the current value of CustomPrepareCode.
(In the database, CustomPrepareCode is stored as text.)
-=head2 SetCustomPrepareCode VALUE
+=item SetCustomPrepareCode VALUE
Set CustomPrepareCode to VALUE.
@@ -308,14 +283,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 CustomCommitCode
+=item CustomCommitCode
Returns the current value of CustomCommitCode.
(In the database, CustomCommitCode is stored as text.)
-=head2 SetCustomCommitCode VALUE
+=item SetCustomCommitCode VALUE
Set CustomCommitCode to VALUE.
@@ -326,14 +301,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Stage
+=item Stage
Returns the current value of Stage.
(In the database, Stage is stored as varchar(32).)
-=head2 SetStage VALUE
+=item SetStage VALUE
Set Stage to VALUE.
@@ -344,14 +319,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Queue
+=item Queue
Returns the current value of Queue.
(In the database, Queue is stored as int(11).)
-=head2 SetQueue VALUE
+=item SetQueue VALUE
Set Queue to VALUE.
@@ -362,7 +337,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 QueueObj
+=item QueueObj
Returns the Queue Object which has the id returned by Queue
@@ -376,14 +351,14 @@ sub QueueObj {
return($Queue);
}
-=head2 Template
+=item Template
Returns the current value of Template.
(In the database, Template is stored as int(11).)
-=head2 SetTemplate VALUE
+=item SetTemplate VALUE
Set Template to VALUE.
@@ -394,7 +369,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 TemplateObj
+=item TemplateObj
Returns the Template Object which has the id returned by Template
@@ -408,7 +383,7 @@ sub TemplateObj {
return($Template);
}
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -417,7 +392,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -426,7 +401,7 @@ Returns the current value of Created.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -435,7 +410,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -445,41 +420,41 @@ Returns the current value of LastUpdated.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
ScripCondition =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
ScripAction =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
ConditionRules =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
ActionRules =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
CustomIsApplicableCode =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
CustomPrepareCode =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
CustomCommitCode =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
Stage =>
- {read => 1, write => 1, sql_type => 12, length => 32, is_blob => 0, is_numeric => 0, type => 'varchar(32)', default => ''},
+ {read => 1, write => 1, type => 'varchar(32)', default => ''},
Queue =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Template =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -511,7 +486,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/ScripAction.pm b/rt/lib/RT/ScripAction.pm
index 87a2613f2..26824df5d 100755
--- a/rt/lib/RT/ScripAction.pm
+++ b/rt/lib/RT/ScripAction.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -120,7 +95,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -129,14 +104,14 @@ Returns the current value of id.
=cut
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -147,14 +122,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -165,14 +140,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ExecModule
+=item ExecModule
Returns the current value of ExecModule.
(In the database, ExecModule is stored as varchar(60).)
-=head2 SetExecModule VALUE
+=item SetExecModule VALUE
Set ExecModule to VALUE.
@@ -183,14 +158,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Argument
+=item Argument
Returns the current value of Argument.
(In the database, Argument is stored as varchar(255).)
-=head2 SetArgument VALUE
+=item SetArgument VALUE
Set Argument to VALUE.
@@ -201,7 +176,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -210,7 +185,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -219,7 +194,7 @@ Returns the current value of Created.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -228,7 +203,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -238,27 +213,27 @@ Returns the current value of LastUpdated.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
ExecModule =>
- {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''},
+ {read => 1, write => 1, type => 'varchar(60)', default => ''},
Argument =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -290,7 +265,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/ScripActions.pm b/rt/lib/RT/ScripActions.pm
index 1f6fb9815..614ff374f 100755
--- a/rt/lib/RT/ScripActions.pm
+++ b/rt/lib/RT/ScripActions.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::ScripAction item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/ScripCondition.pm b/rt/lib/RT/ScripCondition.pm
index 072a24ae5..fe0aa2d5a 100755
--- a/rt/lib/RT/ScripCondition.pm
+++ b/rt/lib/RT/ScripCondition.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -123,7 +98,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -132,14 +107,14 @@ Returns the current value of id.
=cut
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -150,14 +125,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -168,14 +143,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ExecModule
+=item ExecModule
Returns the current value of ExecModule.
(In the database, ExecModule is stored as varchar(60).)
-=head2 SetExecModule VALUE
+=item SetExecModule VALUE
Set ExecModule to VALUE.
@@ -186,14 +161,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Argument
+=item Argument
Returns the current value of Argument.
(In the database, Argument is stored as varchar(255).)
-=head2 SetArgument VALUE
+=item SetArgument VALUE
Set Argument to VALUE.
@@ -204,14 +179,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ApplicableTransTypes
+=item ApplicableTransTypes
Returns the current value of ApplicableTransTypes.
(In the database, ApplicableTransTypes is stored as varchar(60).)
-=head2 SetApplicableTransTypes VALUE
+=item SetApplicableTransTypes VALUE
Set ApplicableTransTypes to VALUE.
@@ -222,7 +197,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -231,7 +206,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -240,7 +215,7 @@ Returns the current value of Created.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -249,7 +224,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -259,29 +234,29 @@ Returns the current value of LastUpdated.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
ExecModule =>
- {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''},
+ {read => 1, write => 1, type => 'varchar(60)', default => ''},
Argument =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
ApplicableTransTypes =>
- {read => 1, write => 1, sql_type => 12, length => 60, is_blob => 0, is_numeric => 0, type => 'varchar(60)', default => ''},
+ {read => 1, write => 1, type => 'varchar(60)', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -313,7 +288,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/ScripConditions.pm b/rt/lib/RT/ScripConditions.pm
index 43ea86d51..34f788d9c 100755
--- a/rt/lib/RT/ScripConditions.pm
+++ b/rt/lib/RT/ScripConditions.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::ScripCondition item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Scrips.pm b/rt/lib/RT/Scrips.pm
index 02d1019a3..a39443136 100755
--- a/rt/lib/RT/Scrips.pm
+++ b/rt/lib/RT/Scrips.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Scrip item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/SearchBuilder.pm b/rt/lib/RT/SearchBuilder.pm
index f6b3571b7..fd0842419 100644
--- a/rt/lib/RT/SearchBuilder.pm
+++ b/rt/lib/RT/SearchBuilder.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::SearchBuilder - a baseclass for RT collection objects
@@ -58,6 +57,11 @@
=head1 METHODS
+=begin testing
+
+ok (require RT::SearchBuilder);
+
+=end testing
=cut
@@ -65,13 +69,13 @@
package RT::SearchBuilder;
use RT::Base;
-use DBIx::SearchBuilder "1.40";
+use DBIx::SearchBuilder "1.50";
use strict;
-use warnings;
-
-use base qw(DBIx::SearchBuilder RT::Base);
+use vars qw(@ISA);
+@ISA = qw(DBIx::SearchBuilder RT::Base);
+# {{{ sub _Init
sub _Init {
my $self = shift;
@@ -84,10 +88,13 @@ sub _Init {
}
$self->SUPER::_Init( 'Handle' => $RT::Handle);
}
+# }}}
+
+# {{{ sub LimitToEnabled
=head2 LimitToEnabled
-Only find items that haven't been disabled
+Only find items that haven\'t been disabled
=cut
@@ -98,6 +105,9 @@ sub LimitToEnabled {
VALUE => '0',
OPERATOR => '=' );
}
+# }}}
+
+# {{{ sub LimitToDisabled
=head2 LimitToDeleted
@@ -114,6 +124,9 @@ sub LimitToDeleted {
VALUE => '1'
);
}
+# }}}
+
+# {{{ sub LimitAttribute
=head2 LimitAttribute PARAMHASH
@@ -127,17 +140,17 @@ If NULL is set, also select rows without the named Attribute.
=cut
-my %Negate = (
- '=' => '!=',
- '!=' => '=',
- '>' => '<=',
- '<' => '>=',
- '>=' => '<',
- '<=' => '>',
- 'LIKE' => 'NOT LIKE',
- 'NOT LIKE' => 'LIKE',
- 'IS' => 'IS NOT',
- 'IS NOT' => 'IS',
+my %Negate = qw(
+ = !=
+ != =
+ > <=
+ < >=
+ >= <
+ <= >
+ LIKE NOT LIKE
+ NOT LIKE LIKE
+ IS IS NOT
+ IS NOT IS
);
sub LimitAttribute {
@@ -204,6 +217,9 @@ sub LimitAttribute {
VALUE => 'NULL',
) if $args{NULL};
}
+# }}}
+
+# {{{ sub LimitCustomField
=head2 LimitCustomField
@@ -262,6 +278,8 @@ sub LimitCustomField {
);
}
+# {{{ sub FindAllRows
+
=head2 FindAllRows
Find all matching rows, regardless of whether they are disabled or not
@@ -272,6 +290,8 @@ sub FindAllRows {
shift->{'find_disabled_rows'} = 1;
}
+# {{{ sub Limit
+
=head2 Limit PARAMHASH
This Limit sub calls SUPER::Limit, but defaults "CASESENSITIVE" to 1, thus
@@ -288,6 +308,10 @@ sub Limit {
return $self->SUPER::Limit(%args);
}
+# }}}
+
+# {{{ sub ItemsOrderBy
+
=head2 ItemsOrderBy
If it has a SortOrder attribute, sort the array by SortOrder.
@@ -311,21 +335,55 @@ sub ItemsOrderBy {
return $items;
}
+# }}}
+
+# {{{ sub ItemsArrayRef
+
=head2 ItemsArrayRef
Return this object's ItemsArray, in the order that ItemsOrderBy sorts
it.
+=begin testing
+
+use_ok(RT::Queues);
+ok(my $queues = RT::Queues->new($RT::SystemUser), 'Created a queues object');
+ok( $queues->UnLimit(),'Unlimited the result set of the queues object');
+my $items = $queues->ItemsArrayRef();
+my @items = @{$items};
+
+ok($queues->NewItem->_Accessible('Name','read'));
+my @sorted = sort {lc($a->Name) cmp lc($b->Name)} @items;
+ok (@sorted, "We have an array of queues, sorted". join(',',map {$_->Name} @sorted));
+
+ok (@items, "We have an array of queues, raw". join(',',map {$_->Name} @items));
+my @sorted_ids = map {$_->id } @sorted;
+my @items_ids = map {$_->id } @items;
+
+is ($#sorted, $#items);
+is ($sorted[0]->Name, $items[0]->Name);
+is ($sorted[-1]->Name, $items[-1]->Name);
+is_deeply(\@items_ids, \@sorted_ids, "ItemsArrayRef sorts alphabetically by name");;
+
+
+=end testing
+
=cut
sub ItemsArrayRef {
my $self = shift;
+ my @items;
+
return $self->ItemsOrderBy($self->SUPER::ItemsArrayRef());
}
+# }}}
+
eval "require RT::SearchBuilder_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/SearchBuilder_Vendor.pm});
eval "require RT::SearchBuilder_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/SearchBuilder_Local.pm});
1;
+
+
diff --git a/rt/lib/RT/Template.pm b/rt/lib/RT/Template.pm
index f27a0c27e..f73ea3ed6 100755
--- a/rt/lib/RT/Template.pm
+++ b/rt/lib/RT/Template.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -87,7 +62,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -130,7 +105,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -139,14 +114,14 @@ Returns the current value of id.
=cut
-=head2 Queue
+=item Queue
Returns the current value of Queue.
(In the database, Queue is stored as int(11).)
-=head2 SetQueue VALUE
+=item SetQueue VALUE
Set Queue to VALUE.
@@ -157,7 +132,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 QueueObj
+=item QueueObj
Returns the Queue Object which has the id returned by Queue
@@ -171,14 +146,14 @@ sub QueueObj {
return($Queue);
}
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -189,14 +164,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Description
+=item Description
Returns the current value of Description.
(In the database, Description is stored as varchar(255).)
-=head2 SetDescription VALUE
+=item SetDescription VALUE
Set Description to VALUE.
@@ -207,14 +182,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Type
+=item Type
Returns the current value of Type.
(In the database, Type is stored as varchar(16).)
-=head2 SetType VALUE
+=item SetType VALUE
Set Type to VALUE.
@@ -225,14 +200,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Language
+=item Language
Returns the current value of Language.
(In the database, Language is stored as varchar(16).)
-=head2 SetLanguage VALUE
+=item SetLanguage VALUE
Set Language to VALUE.
@@ -243,14 +218,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 TranslationOf
+=item TranslationOf
Returns the current value of TranslationOf.
(In the database, TranslationOf is stored as int(11).)
-=head2 SetTranslationOf VALUE
+=item SetTranslationOf VALUE
Set TranslationOf to VALUE.
@@ -261,14 +236,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Content
+=item Content
Returns the current value of Content.
(In the database, Content is stored as blob.)
-=head2 SetContent VALUE
+=item SetContent VALUE
Set Content to VALUE.
@@ -279,7 +254,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -288,7 +263,7 @@ Returns the current value of LastUpdated.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -297,7 +272,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -306,7 +281,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -316,33 +291,33 @@ Returns the current value of Created.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Queue =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Description =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
Type =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
Language =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
TranslationOf =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Content =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ {read => 1, write => 1, type => 'blob', default => ''},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -374,7 +349,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Templates.pm b/rt/lib/RT/Templates.pm
index 898870249..37db84086 100755
--- a/rt/lib/RT/Templates.pm
+++ b/rt/lib/RT/Templates.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Template item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Ticket.pm b/rt/lib/RT/Ticket.pm
index 4dcd189a8..2f075a20c 100755
--- a/rt/lib/RT/Ticket.pm
+++ b/rt/lib/RT/Ticket.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
+# 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.
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -87,7 +62,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -141,7 +116,7 @@ sub Create {
Resolved => '',
Disabled => '0',
- @_);
+ @_);
$self->SUPER::Create(
EffectiveId => $args{'EffectiveId'},
Queue => $args{'Queue'},
@@ -169,7 +144,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -178,14 +153,14 @@ Returns the current value of id.
=cut
-=head2 EffectiveId
+=item EffectiveId
Returns the current value of EffectiveId.
(In the database, EffectiveId is stored as int(11).)
-=head2 SetEffectiveId VALUE
+=item SetEffectiveId VALUE
Set EffectiveId to VALUE.
@@ -196,14 +171,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Queue
+=item Queue
Returns the current value of Queue.
(In the database, Queue is stored as int(11).)
-=head2 SetQueue VALUE
+=item SetQueue VALUE
Set Queue to VALUE.
@@ -214,7 +189,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 QueueObj
+=item QueueObj
Returns the Queue Object which has the id returned by Queue
@@ -222,22 +197,20 @@ Returns the Queue Object which has the id returned by Queue
=cut
sub QueueObj {
- my $self = shift;
- my $Queue = RT::Queue->new($self->CurrentUser);
- $Queue->Load($self->__Value('Queue'));
- return($Queue);
+ my $self = shift;
+ my $Queue = RT::Queue->new($self->CurrentUser);
+ $Queue->Load($self->__Value('Queue'));
+ return($Queue);
}
-
-
-=head2 Type
+=item Type
Returns the current value of Type.
(In the database, Type is stored as varchar(16).)
-=head2 SetType VALUE
+=item SetType VALUE
Set Type to VALUE.
@@ -248,14 +221,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 IssueStatement
+=item IssueStatement
Returns the current value of IssueStatement.
(In the database, IssueStatement is stored as int(11).)
-=head2 SetIssueStatement VALUE
+=item SetIssueStatement VALUE
Set IssueStatement to VALUE.
@@ -266,14 +239,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Resolution
+=item Resolution
Returns the current value of Resolution.
(In the database, Resolution is stored as int(11).)
-=head2 SetResolution VALUE
+=item SetResolution VALUE
Set Resolution to VALUE.
@@ -284,14 +257,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Owner
+=item Owner
Returns the current value of Owner.
(In the database, Owner is stored as int(11).)
-=head2 SetOwner VALUE
+=item SetOwner VALUE
Set Owner to VALUE.
@@ -302,14 +275,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Subject
+=item Subject
Returns the current value of Subject.
(In the database, Subject is stored as varchar(200).)
-=head2 SetSubject VALUE
+=item SetSubject VALUE
Set Subject to VALUE.
@@ -320,14 +293,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 InitialPriority
+=item InitialPriority
Returns the current value of InitialPriority.
(In the database, InitialPriority is stored as int(11).)
-=head2 SetInitialPriority VALUE
+=item SetInitialPriority VALUE
Set InitialPriority to VALUE.
@@ -338,14 +311,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 FinalPriority
+=item FinalPriority
Returns the current value of FinalPriority.
(In the database, FinalPriority is stored as int(11).)
-=head2 SetFinalPriority VALUE
+=item SetFinalPriority VALUE
Set FinalPriority to VALUE.
@@ -356,14 +329,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Priority
+=item Priority
Returns the current value of Priority.
(In the database, Priority is stored as int(11).)
-=head2 SetPriority VALUE
+=item SetPriority VALUE
Set Priority to VALUE.
@@ -374,14 +347,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 TimeEstimated
+=item TimeEstimated
Returns the current value of TimeEstimated.
(In the database, TimeEstimated is stored as int(11).)
-=head2 SetTimeEstimated VALUE
+=item SetTimeEstimated VALUE
Set TimeEstimated to VALUE.
@@ -392,14 +365,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 TimeWorked
+=item TimeWorked
Returns the current value of TimeWorked.
(In the database, TimeWorked is stored as int(11).)
-=head2 SetTimeWorked VALUE
+=item SetTimeWorked VALUE
Set TimeWorked to VALUE.
@@ -410,14 +383,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Status
+=item Status
Returns the current value of Status.
(In the database, Status is stored as varchar(10).)
-=head2 SetStatus VALUE
+=item SetStatus VALUE
Set Status to VALUE.
@@ -428,14 +401,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 TimeLeft
+=item TimeLeft
Returns the current value of TimeLeft.
(In the database, TimeLeft is stored as int(11).)
-=head2 SetTimeLeft VALUE
+=item SetTimeLeft VALUE
Set TimeLeft to VALUE.
@@ -446,14 +419,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Told
+=item Told
Returns the current value of Told.
(In the database, Told is stored as datetime.)
-=head2 SetTold VALUE
+=item SetTold VALUE
Set Told to VALUE.
@@ -464,14 +437,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Starts
+=item Starts
Returns the current value of Starts.
(In the database, Starts is stored as datetime.)
-=head2 SetStarts VALUE
+=item SetStarts VALUE
Set Starts to VALUE.
@@ -482,14 +455,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Started
+=item Started
Returns the current value of Started.
(In the database, Started is stored as datetime.)
-=head2 SetStarted VALUE
+=item SetStarted VALUE
Set Started to VALUE.
@@ -500,14 +473,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Due
+=item Due
Returns the current value of Due.
(In the database, Due is stored as datetime.)
-=head2 SetDue VALUE
+=item SetDue VALUE
Set Due to VALUE.
@@ -518,14 +491,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Resolved
+=item Resolved
Returns the current value of Resolved.
(In the database, Resolved is stored as datetime.)
-=head2 SetResolved VALUE
+=item SetResolved VALUE
Set Resolved to VALUE.
@@ -536,7 +509,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -545,7 +518,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -554,7 +527,7 @@ Returns the current value of LastUpdated.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -563,7 +536,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -572,14 +545,14 @@ Returns the current value of Created.
=cut
-=head2 Disabled
+=item Disabled
Returns the current value of Disabled.
(In the database, Disabled is stored as smallint(6).)
-=head2 SetDisabled VALUE
+=item SetDisabled VALUE
Set Disabled to VALUE.
@@ -591,59 +564,59 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
EffectiveId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Queue =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Type =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
IssueStatement =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Resolution =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Owner =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Subject =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => '[no subject]'},
+ {read => 1, write => 1, type => 'varchar(200)', default => '[no subject]'},
InitialPriority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
FinalPriority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Priority =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
TimeEstimated =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
TimeWorked =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Status =>
- {read => 1, write => 1, sql_type => 12, length => 10, is_blob => 0, is_numeric => 0, type => 'varchar(10)', default => ''},
+ {read => 1, write => 1, type => 'varchar(10)', default => ''},
TimeLeft =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Told =>
- {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, write => 1, type => 'datetime', default => ''},
Starts =>
- {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, write => 1, type => 'datetime', default => ''},
Started =>
- {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, write => 1, type => 'datetime', default => ''},
Due =>
- {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, write => 1, type => 'datetime', default => ''},
Resolved =>
- {read => 1, write => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, write => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
Disabled =>
- {read => 1, write => 1, sql_type => 5, length => 6, is_blob => 0, is_numeric => 1, type => 'smallint(6)', default => '0'},
+ {read => 1, write => 1, type => 'smallint(6)', default => '0'},
}
};
@@ -675,7 +648,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/TicketCustomFieldValue.pm b/rt/lib/RT/TicketCustomFieldValue.pm
deleted file mode 100644
index 717647266..000000000
--- a/rt/lib/RT/TicketCustomFieldValue.pm
+++ /dev/null
@@ -1,308 +0,0 @@
-# {{{ BEGIN BPS TAGGED BLOCK
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# }}} END BPS TAGGED BLOCK
-# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
-# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
-#
-# !! DO NOT EDIT THIS FILE !!
-#
-
-use strict;
-
-
-=head1 NAME
-
-RT::TicketCustomFieldValue
-
-
-=head1 SYNOPSIS
-
-=head1 DESCRIPTION
-
-=head1 METHODS
-
-=cut
-
-package RT::TicketCustomFieldValue;
-use RT::Record;
-use RT::CustomField;
-use RT::Ticket;
-
-
-use vars qw( @ISA );
-@ISA= qw( RT::Record );
-
-sub _Init {
- my $self = shift;
-
- $self->Table('TicketCustomFieldValues');
- $self->SUPER::_Init(@_);
-}
-
-
-
-
-
-=head2 Create PARAMHASH
-
-Create takes a hash of values and creates a row in the database:
-
- int(11) 'Ticket'.
- int(11) 'CustomField'.
- varchar(255) 'Content'.
-
-=cut
-
-
-
-
-sub Create {
- my $self = shift;
- my %args = (
- Ticket => '0',
- CustomField => '0',
- Content => '',
-
- @_);
- $self->SUPER::Create(
- Ticket => $args{'Ticket'},
- CustomField => $args{'CustomField'},
- Content => $args{'Content'},
-);
-
-}
-
-
-
-=head2 id
-
-Returns the current value of id.
-(In the database, id is stored as int(11).)
-
-
-=cut
-
-
-=head2 Ticket
-
-Returns the current value of Ticket.
-(In the database, Ticket is stored as int(11).)
-
-
-
-=head2 SetTicket VALUE
-
-
-Set Ticket to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, Ticket will be stored as a int(11).)
-
-
-=cut
-
-
-=head2 TicketObj
-
-Returns the Ticket Object which has the id returned by Ticket
-
-
-=cut
-
-sub TicketObj {
- my $self = shift;
- my $Ticket = RT::Ticket->new($self->CurrentUser);
- $Ticket->Load($self->__Value('Ticket'));
- return($Ticket);
-}
-
-=head2 CustomField
-
-Returns the current value of CustomField.
-(In the database, CustomField is stored as int(11).)
-
-
-
-=head2 SetCustomField VALUE
-
-
-Set CustomField to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, CustomField will be stored as a int(11).)
-
-
-=cut
-
-
-=head2 CustomFieldObj
-
-Returns the CustomField Object which has the id returned by CustomField
-
-
-=cut
-
-sub CustomFieldObj {
- my $self = shift;
- my $CustomField = RT::CustomField->new($self->CurrentUser);
- $CustomField->Load($self->__Value('CustomField'));
- return($CustomField);
-}
-
-=head2 Content
-
-Returns the current value of Content.
-(In the database, Content is stored as varchar(255).)
-
-
-
-=head2 SetContent VALUE
-
-
-Set Content to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, Content will be stored as a varchar(255).)
-
-
-=cut
-
-
-=head2 Creator
-
-Returns the current value of Creator.
-(In the database, Creator is stored as int(11).)
-
-
-=cut
-
-
-=head2 Created
-
-Returns the current value of Created.
-(In the database, Created is stored as datetime.)
-
-
-=cut
-
-
-=head2 LastUpdatedBy
-
-Returns the current value of LastUpdatedBy.
-(In the database, LastUpdatedBy is stored as int(11).)
-
-
-=cut
-
-
-=head2 LastUpdated
-
-Returns the current value of LastUpdated.
-(In the database, LastUpdated is stored as datetime.)
-
-
-=cut
-
-
-
-sub _CoreAccessible {
- {
-
- id =>
- {read => 1, type => 'int(11)', default => ''},
- Ticket =>
- {read => 1, write => 1, type => 'int(11)', default => '0'},
- CustomField =>
- {read => 1, write => 1, type => 'int(11)', default => '0'},
- Content =>
- {read => 1, write => 1, type => 'varchar(255)', default => ''},
- Creator =>
- {read => 1, auto => 1, type => 'int(11)', default => '0'},
- Created =>
- {read => 1, auto => 1, type => 'datetime', default => ''},
- LastUpdatedBy =>
- {read => 1, auto => 1, type => 'int(11)', default => '0'},
- LastUpdated =>
- {read => 1, auto => 1, type => 'datetime', default => ''},
-
- }
-};
-
-
- eval "require RT::TicketCustomFieldValue_Overlay";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValue_Overlay.pm}) {
- die $@;
- };
-
- eval "require RT::TicketCustomFieldValue_Vendor";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValue_Vendor.pm}) {
- die $@;
- };
-
- eval "require RT::TicketCustomFieldValue_Local";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValue_Local.pm}) {
- 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,
-_Vendor is for 3rd-party vendor add-ons, 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::TicketCustomFieldValue_Overlay, RT::TicketCustomFieldValue_Vendor, RT::TicketCustomFieldValue_Local
-
-=cut
-
-
-1;
diff --git a/rt/lib/RT/TicketCustomFieldValue_Overlay.pm b/rt/lib/RT/TicketCustomFieldValue_Overlay.pm
deleted file mode 100644
index 270c5939a..000000000
--- a/rt/lib/RT/TicketCustomFieldValue_Overlay.pm
+++ /dev/null
@@ -1,74 +0,0 @@
-# {{{ BEGIN BPS TAGGED BLOCK
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# }}} END BPS TAGGED BLOCK
-use strict;
-no warnings qw(redefine);
-
-
-
-=head2 LoadByTicketContentAndCustomField { Ticket => TICKET, CustomField => CUSTOMFIELD, Content => CONTENT }
-
-Loads a custom field value by Ticket, Content and which CustomField it's tied to
-
-=cut
-
-
-sub LoadByTicketContentAndCustomField {
- my $self = shift;
- my %args = ( Ticket => undef,
- CustomField => undef,
- Content => undef,
- @_
- );
-
-
- $self->LoadByCols( Content => $args{'Content'},
- CustomField => $args{'CustomField'},
- Ticket => $args{'Ticket'});
-
-
-}
-
-1;
diff --git a/rt/lib/RT/TicketCustomFieldValues.pm b/rt/lib/RT/TicketCustomFieldValues.pm
deleted file mode 100644
index 2174afef3..000000000
--- a/rt/lib/RT/TicketCustomFieldValues.pm
+++ /dev/null
@@ -1,137 +0,0 @@
-# {{{ BEGIN BPS TAGGED BLOCK
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# }}} END BPS TAGGED BLOCK
-# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
-# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
-#
-# !! DO NOT EDIT THIS FILE !!
-#
-
-use strict;
-
-
-=head1 NAME
-
- RT::TicketCustomFieldValues -- Class Description
-
-=head1 SYNOPSIS
-
- use RT::TicketCustomFieldValues
-
-=head1 DESCRIPTION
-
-
-=head1 METHODS
-
-=cut
-
-package RT::TicketCustomFieldValues;
-
-use RT::SearchBuilder;
-use RT::TicketCustomFieldValue;
-
-use vars qw( @ISA );
-@ISA= qw(RT::SearchBuilder);
-
-
-sub _Init {
- my $self = shift;
- $self->{'table'} = 'TicketCustomFieldValues';
- $self->{'primary_key'} = 'id';
-
-
- return ( $self->SUPER::_Init(@_) );
-}
-
-
-=head2 NewItem
-
-Returns an empty new RT::TicketCustomFieldValue item
-
-=cut
-
-sub NewItem {
- my $self = shift;
- return(RT::TicketCustomFieldValue->new($self->CurrentUser));
-}
-
- eval "require RT::TicketCustomFieldValues_Overlay";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValues_Overlay.pm}) {
- die $@;
- };
-
- eval "require RT::TicketCustomFieldValues_Vendor";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValues_Vendor.pm}) {
- die $@;
- };
-
- eval "require RT::TicketCustomFieldValues_Local";
- if ($@ && $@ !~ qr{^Can't locate RT/TicketCustomFieldValues_Local.pm}) {
- 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,
-_Vendor is for 3rd-party vendor add-ons, 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::TicketCustomFieldValues_Overlay, RT::TicketCustomFieldValues_Vendor, RT::TicketCustomFieldValues_Local
-
-=cut
-
-
-1;
diff --git a/rt/lib/RT/TicketCustomFieldValues_Overlay.pm b/rt/lib/RT/TicketCustomFieldValues_Overlay.pm
deleted file mode 100644
index 8cbaca574..000000000
--- a/rt/lib/RT/TicketCustomFieldValues_Overlay.pm
+++ /dev/null
@@ -1,108 +0,0 @@
-# {{{ BEGIN BPS TAGGED BLOCK
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2004 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# }}} END BPS TAGGED BLOCK
-use strict;
-no warnings qw(redefine);
-
-# {{{ sub LimitToCustomField
-
-=head2 LimitToCustomField FIELD
-
-Limits the returned set to values for the custom field with Id FIELD
-
-=cut
-
-sub LimitToCustomField {
- my $self = shift;
- my $cf = shift;
- return ($self->Limit( FIELD => 'CustomField',
- VALUE => $cf,
- OPERATOR => '='));
-
-}
-
-# }}}
-
-# {{{ sub LimitToTicket
-
-=head2 LimitToTicket TICKETID
-
-Limits the returned set to values for the ticket with Id TICKETID
-
-=cut
-
-sub LimitToTicket {
- my $self = shift;
- my $ticket = shift;
- return ($self->Limit( FIELD => 'Ticket',
- VALUE => $ticket,
- OPERATOR => '='));
-
-}
-
-# }}}
-
-
-=sub HasEntry VALUE
-
-Returns true if this CustomFieldValues collection has an entry with content that eq VALUE
-
-=cut
-
-
-sub HasEntry {
- my $self = shift;
- my $value = shift;
-
- #TODO: this could cache and optimize a fair bit.
- foreach my $item (@{$self->ItemsArrayRef}) {
- return(1) if ($item->Content eq $value);
- }
- return undef;
-
-}
-
-1;
-
diff --git a/rt/lib/RT/Ticket_Overlay.pm b/rt/lib/RT/Ticket_Overlay.pm
index a8d32ef69..20f54625f 100644
--- a/rt/lib/RT/Ticket_Overlay.pm
+++ b/rt/lib/RT/Ticket_Overlay.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
# {{{ Front Material
=head1 SYNOPSIS
@@ -61,6 +60,82 @@ This module lets you manipulate RT\'s ticket object.
=head1 METHODS
+=begin testing
+
+use_ok ( RT::Queue);
+ok(my $testqueue = RT::Queue->new($RT::SystemUser));
+ok($testqueue->Create( Name => 'ticket tests'));
+ok($testqueue->Id != 0);
+use_ok(RT::CustomField);
+ok(my $testcf = RT::CustomField->new($RT::SystemUser));
+my ($ret, $cmsg) = $testcf->Create( Name => 'selectmulti',
+ Queue => $testqueue->id,
+ Type => 'SelectMultiple');
+ok($ret,"Created the custom field - ".$cmsg);
+($ret,$cmsg) = $testcf->AddValue ( Name => 'Value1',
+ SortOrder => '1',
+ Description => 'A testing value');
+
+ok($ret, "Added a value - ".$cmsg);
+
+ok($testcf->AddValue ( Name => 'Value2',
+ SortOrder => '2',
+ Description => 'Another testing value'));
+ok($testcf->AddValue ( Name => 'Value3',
+ SortOrder => '3',
+ Description => 'Yet Another testing value'));
+
+ok($testcf->Values->Count == 3);
+
+use_ok(RT::Ticket);
+
+my $u = RT::User->new($RT::SystemUser);
+$u->Load("root");
+ok ($u->Id, "Found the root user");
+ok(my $t = RT::Ticket->new($RT::SystemUser));
+ok(my ($id, $msg) = $t->Create( Queue => $testqueue->Id,
+ Subject => 'Testing',
+ Owner => $u->Id
+ ));
+ok($id != 0);
+ok ($t->OwnerObj->Id == $u->Id, "Root is the ticket owner");
+ok(my ($cfv, $cfm) =$t->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value1'));
+ok($cfv != 0, "Custom field creation didn't return an error: $cfm");
+ok($t->CustomFieldValues($testcf->Id)->Count == 1);
+ok($t->CustomFieldValues($testcf->Id)->First &&
+ $t->CustomFieldValues($testcf->Id)->First->Content eq 'Value1');;
+
+ok(my ($cfdv, $cfdm) = $t->DeleteCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value1'));
+ok ($cfdv != 0, "Deleted a custom field value: $cfdm");
+ok($t->CustomFieldValues($testcf->Id)->Count == 0);
+
+ok(my $t2 = RT::Ticket->new($RT::SystemUser));
+ok($t2->Load($id));
+is($t2->Subject, 'Testing');
+is($t2->QueueObj->Id, $testqueue->id);
+ok($t2->OwnerObj->Id == $u->Id);
+
+my $t3 = RT::Ticket->new($RT::SystemUser);
+my ($id3, $msg3) = $t3->Create( Queue => $testqueue->Id,
+ Subject => 'Testing',
+ Owner => $u->Id);
+my ($cfv1, $cfm1) = $t->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value1');
+ok($cfv1 != 0, "Adding a custom field to ticket 1 is successful: $cfm");
+my ($cfv2, $cfm2) = $t3->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value2');
+ok($cfv2 != 0, "Adding a custom field to ticket 2 is successful: $cfm");
+my ($cfv3, $cfm3) = $t->AddCustomFieldValue(Field => $testcf->Id,
+ Value => 'Value3');
+ok($cfv3 != 0, "Adding a custom field to ticket 1 is successful: $cfm");
+ok($t->CustomFieldValues($testcf->Id)->Count == 2,
+ "This ticket has 2 custom field values");
+ok($t3->CustomFieldValues($testcf->Id)->Count == 1,
+ "This ticket has 1 custom field value");
+
+=end testing
=cut
@@ -83,20 +158,32 @@ use RT::URI::fsck_com_rt;
use RT::URI;
use MIME::Entity;
+=begin testing
+
+
+ok(require RT::Ticket, "Loading the RT::Ticket library");
+
+=end testing
+
+=cut
+
+# }}}
# {{{ LINKTYPEMAP
# A helper table for links mapping to make it easier
# to build and parse links between tickets
-our %LINKTYPEMAP = (
+use vars '%LINKTYPEMAP';
+
+%LINKTYPEMAP = (
MemberOf => { Type => 'MemberOf',
Mode => 'Target', },
Parents => { Type => 'MemberOf',
- Mode => 'Target', },
+ Mode => 'Target', },
Members => { Type => 'MemberOf',
Mode => 'Base', },
Children => { Type => 'MemberOf',
- Mode => 'Base', },
+ Mode => 'Base', },
HasMember => { Type => 'MemberOf',
Mode => 'Base', },
RefersTo => { Type => 'RefersTo',
@@ -118,7 +205,9 @@ our %LINKTYPEMAP = (
# A helper table for links mapping to make it easier
# to build and parse links between tickets
-our %LINKDIRMAP = (
+use vars '%LINKDIRMAP';
+
+%LINKDIRMAP = (
MemberOf => { Base => 'MemberOf',
Target => 'HasMember', },
RefersTo => { Base => 'RefersTo',
@@ -152,10 +241,9 @@ sub Load {
#TODO modify this routine to look at EffectiveId and do the recursive load
# thing. be careful to cache all the interim tickets we try so we don't loop forever.
- # FIXME: there is no TicketBaseURI option in config
- my $base_uri = RT->Config->Get('TicketBaseURI') || '';
+
#If it's a local URI, turn it into a ticket id
- if ( $base_uri && defined $id && $id =~ /^$base_uri(\d+)$/ ) {
+ if ( $RT::TicketBaseURI && $id =~ /^$RT::TicketBaseURI(\d+)$/ ) {
$id = $1;
}
@@ -165,18 +253,18 @@ sub Load {
}
#If we have an integer URI, load the ticket
- if ( defined $id && $id =~ /^\d+$/ ) {
+ if ( $id =~ /^\d+$/ ) {
my ($ticketid,$msg) = $self->LoadById($id);
unless ($self->Id) {
- $RT::Logger->debug("$self tried to load a bogus ticket: $id");
+ $RT::Logger->crit("$self tried to load a bogus ticket: $id\n");
return (undef);
}
}
#It's not a URI. It's not a numerical ticket ID. Punt!
else {
- $RT::Logger->debug("Tried to load a bogus ticket id: '$id'");
+ $RT::Logger->warning("Tried to load a bogus ticket id: '$id'");
return (undef);
}
@@ -193,6 +281,29 @@ sub Load {
# }}}
+# {{{ sub LoadByURI
+
+=head2 LoadByURI
+
+Given a local ticket URI, loads the specified ticket.
+
+=cut
+
+sub LoadByURI {
+ my $self = shift;
+ my $uri = shift;
+
+ if ( $uri =~ /^$RT::TicketBaseURI(\d+)$/ ) {
+ my $id = $1;
+ return ( $self->Load($id) );
+ }
+ else {
+ return (undef);
+ }
+}
+
+# }}}
+
# {{{ sub Create
=head2 Create (ARGS)
@@ -204,8 +315,6 @@ Arguments: ARGS is a hash of named parameters. Valid parameters are:
Requestor - A reference to a list of email addresses or RT user Names
Cc - A reference to a list of email addresses or Names
AdminCc - A reference to a list of email addresses or Names
- SquelchMailTo - A reference to a list of email addresses -
- who should this ticket not mail
Type -- The ticket\'s type. ignore this for now
Owner -- This ticket\'s owner. either an RT::User object or this user\'s id
Subject -- A string describing the subject of the ticket
@@ -235,6 +344,18 @@ C<Members> and C<Children> are aliases for C<HasMember>.
Returns: TICKETID, Transaction Object, Error Message
+=begin testing
+
+my $t = RT::Ticket->new($RT::SystemUser);
+
+ok( $t->Create(Queue => 'General', Due => '2002-05-21 00:00:00', ReferredToBy => 'http://www.cpan.org', RefersTo => 'http://fsck.com', Subject => 'This is a subject'), "Ticket Created");
+
+ok ( my $id = $t->Id, "Got ticket id");
+ok ($t->RefersTo->First->Target =~ /fsck.com/, "Got refers to");
+ok ($t->ReferredToBy->First->Base =~ /cpan.org/, "Got referredtoby");
+ok ($t->ResolvedObj->Unix == -1, "It hasn't been resolved - ". $t->ResolvedObj->Unix);
+
+=end testing
=cut
@@ -248,7 +369,6 @@ sub Create {
Requestor => undef,
Cc => undef,
AdminCc => undef,
- SquelchMailTo => undef,
Type => 'ticket',
Owner => undef,
Subject => '',
@@ -265,30 +385,30 @@ sub Create {
Resolved => undef,
MIMEObj => undef,
_RecordTransaction => 1,
- DryRun => 0,
@_
);
- my ($ErrStr, @non_fatal_errors);
+ my ( $ErrStr, $Owner, $resolved );
+ my (@non_fatal_errors);
- my $QueueObj = RT::Queue->new( $RT::SystemUser );
- if ( ref $args{'Queue'} eq 'RT::Queue' ) {
- $QueueObj->Load( $args{'Queue'}->Id );
- }
- elsif ( $args{'Queue'} ) {
+ my $QueueObj = RT::Queue->new($RT::SystemUser);
+
+ if ( ( defined( $args{'Queue'} ) ) && ( !ref( $args{'Queue'} ) ) ) {
$QueueObj->Load( $args{'Queue'} );
}
+ elsif ( ref( $args{'Queue'} ) eq 'RT::Queue' ) {
+ $QueueObj->Load( $args{'Queue'}->Id );
+ }
else {
- $RT::Logger->debug("'". ( $args{'Queue'} ||''). "' not a recognised queue object." );
+ $RT::Logger->debug( $args{'Queue'} . " not a recognised queue object." );
}
#Can't create a ticket without a queue.
- unless ( $QueueObj->Id ) {
+ unless ( defined($QueueObj) && $QueueObj->Id ) {
$RT::Logger->debug("$self No queue given for ticket creation.");
return ( 0, 0, $self->loc('Could not create ticket. Queue not set') );
}
-
#Now that we have a queue, Check the ACLS
unless (
$self->CurrentUser->HasRight(
@@ -307,21 +427,21 @@ sub Create {
}
#Since we have a queue, we can set queue defaults
-
#Initial Priority
+
# If there's no queue default initial priority and it's not set, set it to 0
- $args{'InitialPriority'} = $QueueObj->InitialPriority || 0
- unless defined $args{'InitialPriority'};
+ $args{'InitialPriority'} = ( $QueueObj->InitialPriority || 0 )
+ unless ( $args{'InitialPriority'} );
#Final priority
+
# If there's no queue default final priority and it's not set, set it to 0
- $args{'FinalPriority'} = $QueueObj->FinalPriority || 0
- unless defined $args{'FinalPriority'};
+ $args{'FinalPriority'} = ( $QueueObj->FinalPriority || 0 )
+ unless ( $args{'FinalPriority'} );
# Priority may have changed from InitialPriority, for the case
# where we're importing tickets (eg, from an older RT version.)
- $args{'Priority'} = $args{'InitialPriority'}
- unless defined $args{'Priority'};
+ my $priority = $args{'Priority'} || $args{'InitialPriority'};
# {{{ Dates
#TODO we should see what sort of due date we're getting, rather +
@@ -329,7 +449,8 @@ sub Create {
#Set the due date. if we didn't get fed one, use the queue default due in
my $Due = new RT::Date( $self->CurrentUser );
- if ( defined $args{'Due'} ) {
+
+ if ( $args{'Due'} ) {
$Due->Set( Format => 'ISO', Value => $args{'Due'} );
}
elsif ( my $due_in = $QueueObj->DefaultDueIn ) {
@@ -346,9 +467,6 @@ sub Create {
if ( defined $args{'Started'} ) {
$Started->Set( Format => 'ISO', Value => $args{'Started'} );
}
- elsif ( $args{'Status'} ne 'new' ) {
- $Started->SetToNow;
- }
my $Resolved = new RT::Date( $self->CurrentUser );
if ( defined $args{'Resolved'} ) {
@@ -356,10 +474,10 @@ sub Create {
}
#If the status is an inactive status, set the resolved date
- elsif ( $QueueObj->IsInactiveStatus( $args{'Status'} ) )
+ if ( $QueueObj->IsInactiveStatus( $args{'Status'} ) && !$args{'Resolved'} )
{
$RT::Logger->debug( "Got a ". $args{'Status'}
- ."(inactive) ticket with undefined resolved date. Setting to now."
+ ." ticket with undefined resolved date. Setting to now."
);
$Resolved->SetToNow;
}
@@ -376,42 +494,51 @@ sub Create {
# {{{ Deal with setting the owner
- my $Owner;
if ( ref( $args{'Owner'} ) eq 'RT::User' ) {
- if ( $args{'Owner'}->id ) {
- $Owner = $args{'Owner'};
- } else {
- $RT::Logger->error('passed not loaded owner object');
- push @non_fatal_errors, $self->loc("Invalid owner object");
- $Owner = undef;
- }
+ $Owner = $args{'Owner'};
}
#If we've been handed something else, try to load the user.
elsif ( $args{'Owner'} ) {
$Owner = RT::User->new( $self->CurrentUser );
$Owner->Load( $args{'Owner'} );
- $Owner->LoadByEmail( $args{'Owner'} )
- unless $Owner->Id;
- unless ( $Owner->Id ) {
- push @non_fatal_errors,
+
+ push( @non_fatal_errors,
$self->loc("Owner could not be set.") . " "
- . $self->loc( "User '[_1]' could not be found.", $args{'Owner'} );
- $Owner = undef;
- }
+ . $self->loc( "User '[_1]' could not be found.", $args{'Owner'} )
+ )
+ unless ( $Owner->Id );
}
#If we have a proposed owner and they don't have the right
#to own a ticket, scream about it and make them not the owner
-
- my $DeferOwner;
- if ( $Owner && $Owner->Id != $RT::Nobody->Id
- && !$Owner->HasRight( Object => $QueueObj, Right => 'OwnTicket' ) )
+ if (
+ ( defined($Owner) )
+ and ( $Owner->Id )
+ and ( $Owner->Id != $RT::Nobody->Id )
+ and (
+ !$Owner->HasRight(
+ Object => $QueueObj,
+ Right => 'OwnTicket'
+ )
+ )
+ )
{
- $DeferOwner = $Owner;
- $Owner = undef;
- $RT::Logger->debug('going to deffer setting owner');
+ $RT::Logger->warning( "User "
+ . $Owner->Name . "("
+ . $Owner->id
+ . ") was proposed "
+ . "as a ticket owner but has no rights to own "
+ . "tickets in "
+ . $QueueObj->Name );
+
+ push @non_fatal_errors,
+ $self->loc( "Owner '[_1]' does not have rights to own this ticket.",
+ $Owner->Name
+ );
+
+ $Owner = undef;
}
#If we haven't been handed a valid owner, make it nobody.
@@ -425,24 +552,13 @@ sub Create {
# We attempt to load or create each of the people who might have a role for this ticket
# _outside_ the transaction, so we don't get into ticket creation races
foreach my $type ( "Cc", "AdminCc", "Requestor" ) {
- $args{ $type } = [ $args{ $type } ] unless ref $args{ $type };
- foreach my $watcher ( splice @{ $args{$type} } ) {
- next unless $watcher;
- if ( $watcher =~ /^\d+$/ ) {
- push @{ $args{$type} }, $watcher;
- } else {
- my @addresses = RT::EmailParser->ParseEmailAddress( $watcher );
- foreach my $address( @addresses ) {
- my $user = RT::User->new( $RT::SystemUser );
- my ($uid, $msg) = $user->LoadOrCreateByEmail( $address );
- unless ( $uid ) {
- push @non_fatal_errors,
- $self->loc("Couldn't load or create user: [_1]", $msg);
- } else {
- push @{ $args{$type} }, $user->id;
- }
- }
- }
+ next unless ( defined $args{$type} );
+ foreach my $watcher (
+ ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
+ {
+ my $user = RT::User->new($RT::SystemUser);
+ $user->LoadOrCreateByEmail($watcher)
+ if ( $watcher && $watcher !~ /^\d+$/ );
}
}
@@ -454,7 +570,7 @@ sub Create {
Subject => $args{'Subject'},
InitialPriority => $args{'InitialPriority'},
FinalPriority => $args{'FinalPriority'},
- Priority => $args{'Priority'},
+ Priority => $priority,
Status => $args{'Status'},
TimeWorked => $args{'TimeWorked'},
TimeEstimated => $args{'TimeEstimated'},
@@ -468,21 +584,20 @@ sub Create {
# Parameters passed in during an import that we probably don't want to touch, otherwise
foreach my $attr qw(id Creator Created LastUpdated LastUpdatedBy) {
- $params{$attr} = $args{$attr} if $args{$attr};
+ $params{$attr} = $args{$attr} if ( $args{$attr} );
}
# Delete null integer parameters
foreach my $attr
- qw(TimeWorked TimeLeft TimeEstimated InitialPriority FinalPriority)
- {
+ qw(TimeWorked TimeLeft TimeEstimated InitialPriority FinalPriority) {
delete $params{$attr}
unless ( exists $params{$attr} && $params{$attr} );
}
# Delete the time worked if we're counting it in the transaction
- delete $params{'TimeWorked'} if $args{'_RecordTransaction'};
-
- my ($id,$ticket_message) = $self->SUPER::Create( %params );
+ delete $params{TimeWorked} if $args{'_RecordTransaction'};
+
+ my ($id,$ticket_message) = $self->SUPER::Create( %params);
unless ($id) {
$RT::Logger->crit( "Couldn't create a ticket: " . $ticket_message );
$RT::Handle->Rollback();
@@ -496,9 +611,10 @@ sub Create {
Field => 'EffectiveId',
Value => ( $args{'EffectiveId'} || $id )
);
- unless ( $val ) {
- $RT::Logger->crit("Couldn't set EffectiveId: $msg");
- $RT::Handle->Rollback;
+
+ unless ($val) {
+ $RT::Logger->crit("$self ->Create couldn't set EffectiveId: $msg\n");
+ $RT::Handle->Rollback();
return ( 0, 0,
$self->loc("Ticket could not be created due to an internal error")
);
@@ -515,86 +631,60 @@ sub Create {
);
}
- # Set the owner in the Groups table
- # We denormalize it into the Ticket table too because doing otherwise would
- # kill performance, bigtime. It gets kept in lockstep thanks to the magic of transactionalization
+# Set the owner in the Groups table
+# We denormalize it into the Ticket table too because doing otherwise would
+# kill performance, bigtime. It gets kept in lockstep thanks to the magic of transactionalization
+
$self->OwnerGroup->_AddMember(
PrincipalId => $Owner->PrincipalId,
InsideTransaction => 1
- ) unless $DeferOwner;
-
-
+ );
# {{{ Deal with setting up watchers
foreach my $type ( "Cc", "AdminCc", "Requestor" ) {
- # we know it's an array ref
- foreach my $watcher ( @{ $args{$type} } ) {
-
- # Note that we're using AddWatcher, rather than _AddWatcher, as we
- # actually _want_ that ACL check. Otherwise, random ticket creators
- # could make themselves adminccs and maybe get ticket rights. that would
- # be poor
- my $method = $type eq 'AdminCc'? 'AddWatcher': '_AddWatcher';
-
- my ($val, $msg) = $self->$method(
- Type => $type,
- PrincipalId => $watcher,
- Silent => 1,
- );
- push @non_fatal_errors, $self->loc("Couldn't set [_1] watcher: [_2]", $type, $msg)
- unless $val;
- }
- }
-
- if ($args{'SquelchMailTo'}) {
- my @squelch = ref( $args{'SquelchMailTo'} ) ? @{ $args{'SquelchMailTo'} }
- : $args{'SquelchMailTo'};
- $self->_SquelchMailTo( @squelch );
- }
+ next unless ( defined $args{$type} );
+ foreach my $watcher (
+ ref( $args{$type} ) ? @{ $args{$type} } : ( $args{$type} ) )
+ {
+ # If there is an empty entry in the list, let's get out of here.
+ next unless $watcher;
- # }}}
+ # we reason that all-digits number must be a principal id, not email
+ # this is the only way to can add
+ my $field = 'Email';
+ $field = 'PrincipalId' if $watcher =~ /^\d+$/;
- # {{{ Add all the custom fields
+ my ( $wval, $wmsg );
- foreach my $arg ( keys %args ) {
- next unless $arg =~ /^CustomField-(\d+)$/i;
- my $cfid = $1;
+ if ( $type eq 'AdminCc' ) {
- foreach my $value (
- UNIVERSAL::isa( $args{$arg} => 'ARRAY' ) ? @{ $args{$arg} } : ( $args{$arg} ) )
- {
- next unless defined $value && length $value;
+ # Note that we're using AddWatcher, rather than _AddWatcher, as we
+ # actually _want_ that ACL check. Otherwise, random ticket creators
+ # could make themselves adminccs and maybe get ticket rights. that would
+ # be poor
+ ( $wval, $wmsg ) = $self->AddWatcher(
+ Type => $type,
+ $field => $watcher,
+ Silent => 1
+ );
+ }
+ else {
+ ( $wval, $wmsg ) = $self->_AddWatcher(
+ Type => $type,
+ $field => $watcher,
+ Silent => 1
+ );
+ }
- # Allow passing in uploaded LargeContent etc by hash reference
- my ($status, $msg) = $self->_AddCustomFieldValue(
- (UNIVERSAL::isa( $value => 'HASH' )
- ? %$value
- : (Value => $value)
- ),
- Field => $cfid,
- RecordTransaction => 0,
- );
- push @non_fatal_errors, $msg unless $status;
+ push @non_fatal_errors, $wmsg unless ($wval);
}
}
# }}}
-
# {{{ Deal with setting up links
- # TODO: Adding link may fire scrips on other end and those scrips
- # could create transactions on this ticket before 'Create' transaction.
- #
- # We should implement different schema: record 'Create' transaction,
- # create links and only then fire create transaction's scrips.
- #
- # Ideal variant: add all links without firing scrips, record create
- # transaction and only then fire scrips on the other ends of links.
- #
- # //RUZ
-
foreach my $type ( keys %LINKTYPEMAP ) {
next unless ( defined $args{$type} );
foreach my $link (
@@ -602,7 +692,7 @@ sub Create {
{
# Check rights on the other end of the link if we must
# then run _AddLink that doesn't check for ACLs
- if ( RT->Config->Get( 'StrictLinkACL' ) ) {
+ if ( $RT::StrictLinkACL ) {
my ($val, $msg, $obj) = $self->__GetTicketFromURI( URI => $link );
unless ( $val ) {
push @non_fatal_errors, $msg;
@@ -617,9 +707,7 @@ sub Create {
my ( $wval, $wmsg ) = $self->_AddLink(
Type => $LINKTYPEMAP{$type}->{'Type'},
$LINKTYPEMAP{$type}->{'Mode'} => $link,
- Silent => !$args{'_RecordTransaction'},
- 'Silent'. ( $LINKTYPEMAP{$type}->{'Mode'} eq 'Base'? 'Target': 'Base' )
- => 1,
+ Silent => 1
);
push @non_fatal_errors, $wmsg unless ($wval);
@@ -627,37 +715,100 @@ sub Create {
}
# }}}
- # Now that we've created the ticket and set up its metadata, we can actually go and check OwnTicket on the ticket itself.
- # This might be different than before in cases where extensions like RTIR are doing clever things with RT's ACL system
- if ( $DeferOwner ) {
- if (!$DeferOwner->HasRight( Object => $self, Right => 'OwnTicket')) {
-
- $RT::Logger->warning( "User " . $DeferOwner->Name . "(" . $DeferOwner->id
- . ") was proposed as a ticket owner but has no rights to own "
- . "tickets in " . $QueueObj->Name );
- push @non_fatal_errors, $self->loc(
- "Owner '[_1]' does not have rights to own this ticket.",
- $DeferOwner->Name
- );
- } else {
- $Owner = $DeferOwner;
- $self->__Set(Field => 'Owner', Value => $Owner->id);
+ # {{{ Deal with auto-customer association
+
+ #unless we already have (a) customer(s)...
+ unless ( $self->Customers->Count ) {
+
+ #first find any requestors with emails but *without* customer targets
+ my @NoCust_Requestors =
+ grep { $_->EmailAddress && ! $_->Customers->Count }
+ @{ $self->_Requestors->UserMembersObj->ItemsArrayRef };
+
+ for my $Requestor (@NoCust_Requestors) {
+
+ #perhaps the stuff in here should be in a User method??
+ my @Customers =
+ &RT::URI::freeside::email_search( email=>$Requestor->EmailAddress );
+
+ foreach my $custnum ( map $_->{'custnum'}, @Customers ) {
+
+ ## false laziness w/RT/Interface/Web_Vendor.pm
+ my @link = ( 'Type' => 'MemberOf',
+ 'Target' => "freeside://freeside/cust_main/$custnum",
+ );
+
+ my( $val, $msg ) = $Requestor->_AddLink(@link);
+ #XXX should do something with $msg# push @non_fatal_errors, $msg;
+
+ }
+
+ }
+
+ #find any requestors with customer targets
+
+ my %cust_target = ();
+
+ my @Requestors =
+ grep { $_->Customers->Count }
+ @{ $self->_Requestors->UserMembersObj->ItemsArrayRef };
+
+ foreach my $Requestor ( @Requestors ) {
+ foreach my $cust_link ( @{ $Requestor->Customers->ItemsArrayRef } ) {
+ $cust_target{ $cust_link->Target } = 1;
}
- $self->OwnerGroup->_AddMember(
- PrincipalId => $Owner->PrincipalId,
- InsideTransaction => 1
- );
+ }
+
+ #and then auto-associate this ticket with those customers
+
+ foreach my $cust_target ( keys %cust_target ) {
+
+ my @link = ( 'Type' => 'MemberOf',
+ #'Target' => "freeside://freeside/cust_main/$custnum",
+ 'Target' => $cust_target,
+ );
+
+ my( $val, $msg ) = $self->_AddLink(@link);
+ push @non_fatal_errors, $msg;
+
+ }
+
}
+ # }}}
+
+ # {{{ Add all the custom fields
+
+ foreach my $arg ( keys %args ) {
+ next unless ( $arg =~ /^CustomField-(\d+)$/i );
+ my $cfid = $1;
+ foreach
+ my $value ( UNIVERSAL::isa( $args{$arg} => 'ARRAY' ) ? @{ $args{$arg} } : ( $args{$arg} ) )
+ {
+ next unless ( length($value) );
+
+ # Allow passing in uploaded LargeContent etc by hash reference
+ $self->_AddCustomFieldValue(
+ (UNIVERSAL::isa( $value => 'HASH' )
+ ? %$value
+ : (Value => $value)
+ ),
+ Field => $cfid,
+ RecordTransaction => 0,
+ );
+ }
+ }
+
+ # }}}
+
if ( $args{'_RecordTransaction'} ) {
# {{{ Add a transaction for the create
my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
- Type => "Create",
- TimeTaken => $args{'TimeWorked'},
- MIMEObj => $args{'MIMEObj'},
- CommitScrips => !$args{'DryRun'},
+ Type => "Create",
+ TimeTaken => $args{'TimeWorked'},
+ MIMEObj => $args{'MIMEObj'}
);
if ( $self->Id && $Trans ) {
@@ -676,10 +827,6 @@ sub Create {
return ( 0, 0, $self->loc( "Ticket could not be created due to an internal error"));
}
- if ( $args{'DryRun'} ) {
- $RT::Handle->Rollback();
- return ($self->id, $TransObj, $ErrStr);
- }
$RT::Handle->Commit();
return ( $self->Id, $TransObj->Id, $ErrStr );
@@ -699,6 +846,179 @@ sub Create {
# }}}
+
+# {{{ UpdateFrom822
+
+=head2 UpdateFrom822 $MESSAGE
+
+Takes an RFC822 format message as a string and uses it to make a bunch of changes to a ticket.
+Returns an um. ask me again when the code exists
+
+
+=begin testing
+
+my $simple_update = <<EOF;
+Subject: target
+AddRequestor: jesse\@example.com
+EOF
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($id,$msg) =$ticket->Create(Subject => 'first', Queue => 'general');
+ok($ticket->Id, "Created the test ticket - ".$id ." - ".$msg);
+$ticket->UpdateFrom822($simple_update);
+is($ticket->Subject, 'target', "changed the subject");
+my $jesse = RT::User->new($RT::SystemUser);
+$jesse->LoadByEmail('jesse@example.com');
+ok ($jesse->Id, "There's a user for jesse");
+ok($ticket->Requestors->HasMember( $jesse->PrincipalObj), "It has the jesse principal object as a requestor ");
+
+=end testing
+
+
+=cut
+
+sub UpdateFrom822 {
+ my $self = shift;
+ my $content = shift;
+ my %args = $self->_Parse822HeadersForAttributes($content);
+
+
+ my %ticketargs = (
+ Queue => $args{'queue'},
+ Subject => $args{'subject'},
+ Status => $args{'status'},
+ Due => $args{'due'},
+ Starts => $args{'starts'},
+ Started => $args{'started'},
+ Resolved => $args{'resolved'},
+ Owner => $args{'owner'},
+ Requestor => $args{'requestor'},
+ Cc => $args{'cc'},
+ AdminCc => $args{'admincc'},
+ TimeWorked => $args{'timeworked'},
+ TimeEstimated => $args{'timeestimated'},
+ TimeLeft => $args{'timeleft'},
+ InitialPriority => $args{'initialpriority'},
+ Priority => $args{'priority'},
+ FinalPriority => $args{'finalpriority'},
+ Type => $args{'type'},
+ DependsOn => $args{'dependson'},
+ DependedOnBy => $args{'dependedonby'},
+ RefersTo => $args{'refersto'},
+ ReferredToBy => $args{'referredtoby'},
+ Members => $args{'members'},
+ MemberOf => $args{'memberof'},
+ MIMEObj => $args{'mimeobj'}
+ );
+
+ foreach my $type qw(Requestor Cc Admincc) {
+
+ foreach my $action ( 'Add', 'Del', '' ) {
+
+ my $lctag = lc($action) . lc($type);
+ foreach my $list ( $args{$lctag}, $args{ $lctag . 's' } ) {
+
+ foreach my $entry ( ref($list) ? @{$list} : ($list) ) {
+ push @{$ticketargs{ $action . $type }} , split ( /\s*,\s*/, $entry );
+ }
+
+ }
+
+ # Todo: if we're given an explicit list, transmute it into a list of adds/deletes
+
+ }
+ }
+
+ # Add custom field entries to %ticketargs.
+ # TODO: allow named custom fields
+ map {
+ /^customfield-(\d+)$/
+ && ( $ticketargs{ "CustomField-" . $1 } = $args{$_} );
+ } keys(%args);
+
+# for each ticket we've been told to update, iterate through the set of
+# rfc822 headers and perform that update to the ticket.
+
+
+ # {{{ Set basic fields
+ my @attribs = qw(
+ Subject
+ FinalPriority
+ Priority
+ TimeEstimated
+ TimeWorked
+ TimeLeft
+ Status
+ Queue
+ Type
+ );
+
+
+ # Resolve the queue from a name to a numeric id.
+ if ( $ticketargs{'Queue'} and ( $ticketargs{'Queue'} !~ /^(\d+)$/ ) ) {
+ my $tempqueue = RT::Queue->new($RT::SystemUser);
+ $tempqueue->Load( $ticketargs{'Queue'} );
+ $ticketargs{'Queue'} = $tempqueue->Id() if ( $tempqueue->id );
+ }
+
+ my @results;
+
+ foreach my $attribute (@attribs) {
+ my $value = $ticketargs{$attribute};
+
+ if ( $value ne $self->$attribute() ) {
+
+ my $method = "Set$attribute";
+ my ( $code, $msg ) = $self->$method($value);
+
+ push @results, $self->loc($attribute) . ': ' . $msg;
+
+ }
+ }
+
+ # We special case owner changing, so we can use ForceOwnerChange
+ if ( $ticketargs{'Owner'} && ( $self->Owner != $ticketargs{'Owner'} ) ) {
+ my $ChownType = "Give";
+ $ChownType = "Force" if ( $ticketargs{'ForceOwnerChange'} );
+
+ my ( $val, $msg ) = $self->SetOwner( $ticketargs{'Owner'}, $ChownType );
+ push ( @results, $msg );
+ }
+
+ # }}}
+# Deal with setting watchers
+
+
+# Acceptable arguments:
+# Requestor
+# Requestors
+# AddRequestor
+# AddRequestors
+# DelRequestor
+
+ foreach my $type qw(Requestor Cc AdminCc) {
+
+ # If we've been given a number of delresses to del, do it.
+ foreach my $address (@{$ticketargs{'Del'.$type}}) {
+ my ($id, $msg) = $self->DeleteWatcher( Type => $type, Email => $address);
+ push (@results, $msg) ;
+ }
+
+ # If we've been given a number of addresses to add, do it.
+ foreach my $address (@{$ticketargs{'Add'.$type}}) {
+ $RT::Logger->debug("Adding $address as a $type");
+ my ($id, $msg) = $self->AddWatcher( Type => $type, Email => $address);
+ push (@results, $msg) ;
+
+ }
+
+
+}
+
+
+}
+# }}}
+
# {{{ _Parse822HeadersForAttributes Content
=head2 _Parse822HeadersForAttributes Content
@@ -743,7 +1063,7 @@ sub _Parse822HeadersForAttributes {
foreach my $date qw(due starts started resolved) {
my $dateobj = RT::Date->new($RT::SystemUser);
- if ( defined ($args{$date}) and $args{$date} =~ /^\d+$/ ) {
+ if ( $args{$date} =~ /^\d+$/ ) {
$dateobj->Set( Format => 'unix', Value => $args{$date} );
}
else {
@@ -869,7 +1189,7 @@ sub Import {
. ") was proposed "
. "as a ticket owner but has no rights to own "
. "tickets in '"
- . $QueueObj->Name . "'" );
+ . $QueueObj->Name . "'\n" );
$Owner = undef;
}
@@ -903,18 +1223,18 @@ sub Import {
EffectiveId => $EffectiveId,
Queue => $QueueObj->Id,
Owner => $Owner->Id,
- Subject => $args{'Subject'}, # loc
- InitialPriority => $args{'InitialPriority'}, # loc
- FinalPriority => $args{'FinalPriority'}, # loc
- Priority => $args{'InitialPriority'}, # loc
- Status => $args{'Status'}, # loc
- TimeWorked => $args{'TimeWorked'}, # loc
- Type => $args{'Type'}, # loc
- Created => $args{'Created'}, # loc
- Told => $args{'Told'}, # loc
- LastUpdated => $args{'Updated'}, # loc
- Resolved => $args{'Resolved'}, # loc
- Due => $args{'Due'}, # loc
+ Subject => $args{'Subject'}, # loc
+ InitialPriority => $args{'InitialPriority'}, # loc
+ FinalPriority => $args{'FinalPriority'}, # loc
+ Priority => $args{'InitialPriority'}, # loc
+ Status => $args{'Status'}, # loc
+ TimeWorked => $args{'TimeWorked'}, # loc
+ Type => $args{'Type'}, # loc
+ Created => $args{'Created'}, # loc
+ Told => $args{'Told'}, # loc
+ LastUpdated => $args{'Updated'}, # loc
+ Resolved => $args{'Resolved'}, # loc
+ Due => $args{'Due'}, # loc
);
# If the ticket didn't have an id
@@ -928,7 +1248,7 @@ sub Import {
unless ($val) {
$RT::Logger->err(
- $self . "->Import couldn't set EffectiveId: $msg" );
+ $self . "->Import couldn't set EffectiveId: $msg\n" );
}
}
@@ -971,6 +1291,48 @@ It will create four groups for this ticket: Requestor, Cc, AdminCc and Owner.
It will return true on success and undef on failure.
+=begin testing
+
+my $ticket = RT::Ticket->new($RT::SystemUser);
+my ($id, $msg) = $ticket->Create(Subject => "Foo",
+ Owner => $RT::SystemUser->Id,
+ Status => 'open',
+ Requestor => ['jesse@example.com'],
+ Queue => '1'
+ );
+ok ($id, "Ticket $id was created");
+ok(my $group = RT::Group->new($RT::SystemUser));
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Requestor'));
+ok ($group->Id, "Found the requestors object for this ticket");
+
+ok(my $jesse = RT::User->new($RT::SystemUser), "Creating a jesse rt::user");
+$jesse->LoadByEmail('jesse@example.com');
+ok($jesse->Id, "Found the jesse rt user");
+
+
+ok ($ticket->IsWatcher(Type => 'Requestor', PrincipalId => $jesse->PrincipalId), "The ticket actually has jesse at fsck.com as a requestor");
+ok ((my $add_id, $add_msg) = $ticket->AddWatcher(Type => 'Requestor', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
+ok ($add_id, "Add succeeded: ($add_msg)");
+ok(my $bob = RT::User->new($RT::SystemUser), "Creating a bob rt::user");
+$bob->LoadByEmail('bob@fsck.com');
+ok($bob->Id, "Found the bob rt user");
+ok ($ticket->IsWatcher(Type => 'Requestor', PrincipalId => $bob->PrincipalId), "The ticket actually has bob at fsck.com as a requestor");;
+ok ((my $add_id, $add_msg) = $ticket->DeleteWatcher(Type =>'Requestor', Email => 'bob@fsck.com'), "Added bob at fsck.com as a requestor");
+ok (!$ticket->IsWatcher(Type => 'Requestor', Principal => $bob->PrincipalId), "The ticket no longer has bob at fsck.com as a requestor");;
+
+
+$group = RT::Group->new($RT::SystemUser);
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Cc'));
+ok ($group->Id, "Found the cc object for this ticket");
+$group = RT::Group->new($RT::SystemUser);
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'AdminCc'));
+ok ($group->Id, "Found the AdminCc object for this ticket");
+$group = RT::Group->new($RT::SystemUser);
+ok($group->LoadTicketRoleGroup(Ticket => $id, Type=> 'Owner'));
+ok ($group->Id, "Found the Owner object for this ticket");
+ok($group->HasMember($RT::SystemUser->UserObj->PrincipalObj), "the owner group has the member 'RT_System'");
+
+=end testing
=cut
@@ -1023,12 +1385,12 @@ AddWatcher takes a parameter hash. The keys are as follows:
Type One of Requestor, Cc, AdminCc
-PrincipalId The RT::Principal id of the user or group that's being added as a watcher
+PrinicpalId The RT::Principal id of the user or group that's being added as a watcher
Email The email address of the new watcher. If a user with this
email address can't be found, a new nonprivileged user will be created.
-If the watcher you\'re trying to set has an RT account, set the PrincipalId paremeter to their User Id. Otherwise, set the Email parameter to their Email address.
+If the watcher you\'re trying to set has an RT account, set the Owner paremeter to their User Id. Otherwise, set the Email parameter to their Email address.
=cut
@@ -1041,47 +1403,43 @@ sub AddWatcher {
@_
);
- # ModifyTicket works in any case
- return $self->_AddWatcher( %args )
- if $self->CurrentUserHasRight('ModifyTicket');
- if ( $args{'Email'} ) {
- my ($addr) = RT::EmailParser->ParseEmailAddress( $args{'Email'} );
- return (0, $self->loc("Couldn't parse address from '[_1]' string", $args{'Email'} ))
- unless $addr;
+ return ( 0, "No principal specified" )
+ unless $args{'Email'} or $args{'PrincipalId'};
- if ( lc $self->CurrentUser->UserObj->EmailAddress
- eq lc RT::User->CanonicalizeEmailAddress( $addr->address ) )
- {
- $args{'PrincipalId'} = $self->CurrentUser->id;
+ if ( !$args{'PrincipalId'} and $args{'Email'} ) {
+ my $user = RT::User->new( $self->CurrentUser );
+ $user->LoadByEmail( $args{'Email'} );
+ if ( $user->id ) {
+ $args{'PrincipalId'} = $user->PrincipalId;
delete $args{'Email'};
}
}
- # If the watcher isn't the current user then the current user has no right
- # bail
- unless ( $args{'PrincipalId'} && $self->CurrentUser->id == $args{'PrincipalId'} ) {
- return ( 0, $self->loc("Permission Denied") );
- }
+ # {{{ Check ACLS
+ # ModifyTicket allow you to add any watcher
+ return $self->_AddWatcher(%args)
+ if $self->CurrentUserHasRight('ModifyTicket');
- # If it's an AdminCc and they don't have 'WatchAsAdminCc', bail
- if ( $args{'Type'} eq 'AdminCc' ) {
- unless ( $self->CurrentUserHasRight('WatchAsAdminCc') ) {
- return ( 0, $self->loc('Permission Denied') );
+ #If the watcher we're trying to add is for the current user
+ if ( $self->CurrentUser->PrincipalId == ($args{'PrincipalId'} || 0) ) {
+ # If it's an AdminCc and they have 'WatchAsAdminCc'
+ if ( $args{'Type'} eq 'AdminCc' ) {
+ return $self->_AddWatcher( %args )
+ if $self->CurrentUserHasRight('WatchAsAdminCc');
}
- }
- # If it's a Requestor or Cc and they don't have 'Watch', bail
- elsif ( $args{'Type'} eq 'Cc' || $args{'Type'} eq 'Requestor' ) {
- unless ( $self->CurrentUserHasRight('Watch') ) {
- return ( 0, $self->loc('Permission Denied') );
+ # If it's a Requestor or Cc and they have 'Watch'
+ elsif ( $args{'Type'} eq 'Cc' || $args{'Type'} eq 'Requestor' ) {
+ return $self->_AddWatcher( %args )
+ if $self->CurrentUserHasRight('Watch');
+ }
+ else {
+ $RT::Logger->warning( "AddWatcher got passed a bogus type" );
+ return ( 0, $self->loc('Error in parameters to Ticket->AddWatcher') );
}
- }
- else {
- $RT::Logger->warning( "AddWatcher got passed a bogus type");
- return ( 0, $self->loc('Error in parameters to Ticket->AddWatcher') );
}
- return $self->_AddWatcher( %args );
+ return ( 0, $self->loc("Permission Denied") );
}
#This contains the meat of AddWatcher. but can be called from a routine like
@@ -1100,8 +1458,14 @@ sub _AddWatcher {
my $principal = RT::Principal->new($self->CurrentUser);
if ($args{'Email'}) {
my $user = RT::User->new($RT::SystemUser);
- my ($pid, $msg) = $user->LoadOrCreateByEmail( $args{'Email'} );
- $args{'PrincipalId'} = $pid if $pid;
+ my ($pid, $msg) = $user->LoadOrCreateByEmail($args{'Email'});
+ # If we can't load the user by email address, let's try to load by username
+ unless ($pid) {
+ ($pid,$msg) = $user->Load($args{'Email'})
+ }
+ if ($pid) {
+ $args{'PrincipalId'} = $pid;
+ }
}
if ($args{'PrincipalId'}) {
$principal->Load($args{'PrincipalId'});
@@ -1130,7 +1494,7 @@ sub _AddWatcher {
my ( $m_id, $m_msg ) = $group->_AddMember( PrincipalId => $principal->Id,
InsideTransaction => 1 );
unless ($m_id) {
- $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id.": ".$m_msg);
+ $RT::Logger->error("Failed to add ".$principal->Id." as a member of group ".$group->Id."\n".$m_msg);
return ( 0, $self->loc('Could not make that principal a [_1] for this ticket', $self->loc($args{'Type'})) );
}
@@ -1253,7 +1617,7 @@ sub DeleteWatcher {
$RT::Logger->error( "Failed to delete "
. $principal->Id
. " as a member of group "
- . $group->Id . ": "
+ . $group->Id . "\n"
. $m_msg );
return (0,
@@ -1286,6 +1650,35 @@ Takes an optional email address to never email about updates to this ticket.
Returns an array of the RT::Attribute objects for this ticket's 'SquelchMailTo' attributes.
+=begin testing
+
+my $t = RT::Ticket->new($RT::SystemUser);
+ok($t->Create(Queue => 'general', Subject => 'SquelchTest'));
+
+is($#{$t->SquelchMailTo}, -1, "The ticket has no squelched recipients");
+
+my @returned = $t->SquelchMailTo('nobody@example.com');
+
+is($#returned, 0, "The ticket has one squelched recipients");
+
+my @names = $t->Attributes->Names;
+is(shift @names, 'SquelchMailTo', "The attribute we have is SquelchMailTo");
+@returned = $t->SquelchMailTo('nobody@example.com');
+
+
+is($#returned, 0, "The ticket has one squelched recipients");
+
+@names = $t->Attributes->Names;
+is(shift @names, 'SquelchMailTo', "The attribute we have is SquelchMailTo");
+
+
+my ($ret, $msg) = $t->UnsquelchMailTo('nobody@example.com');
+ok($ret, "Removed nobody as a squelched recipient - ".$msg);
+@returned = $t->SquelchMailTo();
+is($#returned, -1, "The ticket has no squelched recipients". join(',',@returned));
+
+
+=end testing
=cut
@@ -1295,22 +1688,14 @@ sub SquelchMailTo {
unless ( $self->CurrentUserHasRight('ModifyTicket') ) {
return undef;
}
- } else {
- unless ( $self->CurrentUserHasRight('ShowTicket') ) {
- return undef;
- }
-
- }
- return $self->_SquelchMailTo(@_);
-}
-
-sub _SquelchMailTo {
- my $self = shift;
- if (@_) {
my $attr = shift;
$self->AddAttribute( Name => 'SquelchMailTo', Content => $attr )
- unless grep { $_->Content eq $attr }
- $self->Attributes->Named('SquelchMailTo');
+ unless grep { $_->Content eq $attr }
+ $self->Attributes->Named('SquelchMailTo');
+
+ }
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return undef;
}
my @attributes = $self->Attributes->Named('SquelchMailTo');
return (@attributes);
@@ -1386,6 +1771,7 @@ sub CcAddresses {
unless ( $self->CurrentUserHasRight('ShowTicket') ) {
return undef;
}
+
return ( $self->Cc->MemberEmailAddressesAsString);
}
@@ -1416,6 +1802,25 @@ sub Requestors {
# }}}
+# {{{ sub _Requestors
+
+=head2 _Requestors
+
+Private non-ACLed variant of Reqeustors so that we can look them up for the
+purposes of customer auto-association during create.
+
+=cut
+
+sub _Requestors {
+ my $self = shift;
+
+ my $group = RT::Group->new($RT::SystemUser);
+ $group->LoadTicketRoleGroup(Type => 'Requestor', Ticket => $self->Id);
+ return ($group);
+}
+
+# }}}
+
# {{{ sub Cc
=head2 Cc
@@ -1498,6 +1903,7 @@ sub IsWatcher {
$group->LoadTicketRoleGroup(Type => $args{'Type'}, Ticket => $self->id);
# Find the relevant principal.
+ my $principal = RT::Principal->new($self->CurrentUser);
if (!$args{PrincipalId} && $args{Email}) {
# Look up the specified user.
my $user = RT::User->new($self->CurrentUser);
@@ -1510,9 +1916,10 @@ sub IsWatcher {
return 0;
}
}
+ $principal->Load($args{'PrincipalId'});
# Ask if it has the member in question
- return $group->HasMember( $args{'PrincipalId'} );
+ return ($group->HasMember($principal));
}
# }}}
@@ -1521,9 +1928,9 @@ sub IsWatcher {
=head2 IsRequestor PRINCIPAL_ID
-Takes an L<RT::Principal> id.
+ Takes an RT::Principal id
+ Returns true if the principal is a requestor of the current ticket.
-Returns true if the principal is a requestor of the current ticket.
=cut
@@ -1542,7 +1949,7 @@ sub IsRequestor {
=head2 IsCc PRINCIPAL_ID
Takes an RT::Principal id.
- Returns true if the principal is a Cc of the current ticket.
+ Returns true if the principal is a requestor of the current ticket.
=cut
@@ -1562,7 +1969,7 @@ sub IsCc {
=head2 IsAdminCc PRINCIPAL_ID
Takes an RT::Principal id.
- Returns true if the principal is an AdminCc of the current ticket.
+ Returns true if the principal is a requestor of the current ticket.
=cut
@@ -1591,8 +1998,8 @@ sub IsOwner {
# no ACL check since this is used in acl decisions
# unless ($self->CurrentUserHasRight('ShowTicket')) {
- # return(undef);
- # }
+ # return(undef);
+ # }
#Tickets won't yet have owners when they're being created.
unless ( $self->OwnerObj->id ) {
@@ -1613,46 +2020,6 @@ sub IsOwner {
# }}}
-
-=head2 TransactionAddresses
-
-Returns a composite hashref of the results of L<RT::Transaction/Addresses> for all this ticket's Create, Comment or Correspond transactions.
-The keys are C<To>, C<Cc> and C<Bcc>. The values are lists of C<Email::Address> objects.
-
-NOTE: For performance reasons, this method might want to skip transactions and go straight for attachments. But to make that work right, we're going to need to go and walk around the access control in Attachment.pm's sub _Value.
-
-=cut
-
-
-sub TransactionAddresses {
- my $self = shift;
- my $txns = $self->Transactions;
-
- my %addresses = ();
- foreach my $type (qw(Create Comment Correspond)) {
- $txns->Limit(FIELD => 'Type', OPERATOR => '=', VALUE => $type , ENTRYAGGREGATOR => 'OR', CASESENSITIVE => 1);
- }
-
- while (my $txn = $txns->Next) {
- my $txnaddrs = $txn->Addresses;
- foreach my $addrlist ( values %$txnaddrs ) {
- foreach my $addr (@$addrlist) {
- # Skip addresses without a phrase (things that are just raw addresses) if we have a phrase
- next if ($addresses{$addr->address} && $addresses{$addr->address}->phrase && not $addr->phrase);
- # skips "comment-only" addresses
- next unless ($addr->address);
- $addresses{$addr->address} = $addr;
- }
- }
- }
-
- return \%addresses;
-
-}
-
-
-
-
# {{{ Routines dealing with queues
# {{{ sub ValidateQueue
@@ -1726,18 +2093,7 @@ sub SetQueue {
$RT::Logger->error("Couldn't set owner on queue change: $msg") unless $status;
}
- my ($status, $msg) = $self->_Set( Field => 'Queue', Value => $NewQueueObj->Id() );
-
- if ( $status ) {
- # On queue change, change queue for reminders too
- my $reminder_collection = $self->Reminders->Collection;
- while ( my $reminder = $reminder_collection->Next ) {
- my ($status, $msg) = $reminder->SetQueue($NewQueue);
- $RT::Logger->error('Queue change failed for reminder #' . $reminder->Id . ': ' . $msg) unless $status;
- }
- }
-
- return ($status, $msg);
+ return ( $self->_Set( Field => 'Queue', Value => $NewQueueObj->Id() ) );
}
# }}}
@@ -1780,8 +2136,8 @@ sub DueObj {
my $time = new RT::Date( $self->CurrentUser );
# -1 is RT::Date slang for never
- if ( my $due = $self->Due ) {
- $time->Set( Format => 'sql', Value => $due );
+ if ( $self->Due ) {
+ $time->Set( Format => 'sql', Value => $self->Due );
}
else {
$time->Set( Format => 'unix', Value => -1 );
@@ -1960,37 +2316,23 @@ Returns the amount of time worked on this ticket as a Text String
sub TimeWorkedAsString {
my $self = shift;
- my $value = $self->TimeWorked;
+ return "0" unless $self->TimeWorked;
- # return the # of minutes worked turned into seconds and written as
- # a simple text string, this is not really a date object, but if we
- # diff a number of seconds vs the epoch, we'll get a nice description
- # of time worked.
- return "" unless $value;
- return RT::Date->new( $self->CurrentUser )
- ->DurationAsString( $value * 60 );
-}
-
-# }}}
-
-# {{{ sub TimeLeftAsString
+ #This is not really a date object, but if we diff a number of seconds
+ #vs the epoch, we'll get a nice description of time worked.
-=head2 TimeLeftAsString
+ my $worked = new RT::Date( $self->CurrentUser );
-Returns the amount of time left on this ticket as a Text String
+ #return the #of minutes worked turned into seconds and written as
+ # a simple text string
-=cut
-
-sub TimeLeftAsString {
- my $self = shift;
- my $value = $self->TimeLeft;
- return "" unless $value;
- return RT::Date->new( $self->CurrentUser )
- ->DurationAsString( $value * 60 );
+ return ( $worked->DurationAsString( $self->TimeWorked * 60 ) );
}
# }}}
+# }}}
+
# {{{ Routines dealing with correspondence/comments
# {{{ sub Comment
@@ -1998,9 +2340,9 @@ sub TimeLeftAsString {
=head2 Comment
Comment on this ticket.
-Takes a hash with the following attributes:
+Takes a hashref with the following attributes:
If MIMEObj is undefined, Content will be used to build a MIME::Entity for this
-comment.
+commentl
MIMEObj, TimeTaken, CcMessageTo, BccMessageTo, Content, DryRun
@@ -2111,37 +2453,34 @@ Performs no access control checks. hence, dangerous.
=cut
sub _RecordNote {
+
my $self = shift;
- my %args = (
- CcMessageTo => undef,
- BccMessageTo => undef,
- Encrypt => undef,
- Sign => undef,
- MIMEObj => undef,
- Content => undef,
- NoteType => 'Correspond',
- TimeTaken => 0,
- CommitScrips => 1,
- @_
- );
+ my %args = ( CcMessageTo => undef,
+ BccMessageTo => undef,
+ MIMEObj => undef,
+ Content => undef,
+ TimeTaken => 0,
+ CommitScrips => 1,
+ @_ );
unless ( $args{'MIMEObj'} || $args{'Content'} ) {
- return ( 0, $self->loc("No message attached"), undef );
+ return ( 0, $self->loc("No message attached"), undef );
}
-
unless ( $args{'MIMEObj'} ) {
- $args{'MIMEObj'} = MIME::Entity->build(
- Data => ( ref $args{'Content'}? $args{'Content'}: [ $args{'Content'} ] )
- );
- }
+ $args{'MIMEObj'} = MIME::Entity->build( Data => (
+ ref $args{'Content'}
+ ? $args{'Content'}
+ : [ $args{'Content'} ]
+ ) );
+ }
# convert text parts into utf-8
RT::I18N::SetMIMEEntityToUTF8( $args{'MIMEObj'} );
- # If we've been passed in CcMessageTo and BccMessageTo fields,
- # add them to the mime object for passing on to the transaction handler
- # The "NotifyOtherRecipients" scripAction will look for RT-Send-Cc: and
- # RT-Send-Bcc: headers
+# If we've been passed in CcMessageTo and BccMessageTo fields,
+# add them to the mime object for passing on to the transaction handler
+# The "NotifyOtherRecipients" scripAction will look for RT-Send-Cc: and RT-Send-Bcc:
+# headers
foreach my $type (qw/Cc Bcc/) {
@@ -2149,26 +2488,28 @@ sub _RecordNote {
my $addresses = join ', ', (
map { RT::User->CanonicalizeEmailAddress( $_->address ) }
- Email::Address->parse( $args{ $type . 'MessageTo' } ) );
+ Mail::Address->parse( $args{ $type . 'MessageTo' } ) );
$args{'MIMEObj'}->head->add( 'RT-Send-' . $type, $addresses );
}
}
- foreach my $argument (qw(Encrypt Sign)) {
- $args{'MIMEObj'}->head->add(
- "X-RT-$argument" => $args{ $argument }
- ) if defined $args{ $argument };
- }
-
# If this is from an external source, we need to come up with its
# internal Message-ID now, so all emails sent because of this
# message have a common Message-ID
- my $org = RT->Config->Get('Organization');
- my $msgid = $args{'MIMEObj'}->head->get('Message-ID');
- unless (defined $msgid && $msgid =~ /<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@\Q$org\E>/) {
- $args{'MIMEObj'}->head->set(
- 'RT-Message-ID' => RT::Interface::Email::GenMessageId( Ticket => $self )
- );
+ unless ( ($args{'MIMEObj'}->head->get('Message-ID') || '')
+ =~ /<(rt-.*?-\d+-\d+)\.(\d+-0-0)\@\Q$RT::Organization>/ )
+ {
+ $args{'MIMEObj'}->head->replace( 'RT-Message-ID',
+ "<rt-"
+ . $RT::VERSION . "-"
+ . $$ . "-"
+ . CORE::time() . "-"
+ . int(rand(2000)) . '.'
+ . $self->id . "-"
+ . "0" . "-" # Scrip
+ . "0" . "@" # Email sent
+ . $RT::Organization
+ . ">" );
}
#Record the correspondence (write the transaction)
@@ -2204,7 +2545,13 @@ sub _Links {
unless ( $self->{"$field$type"} ) {
$self->{"$field$type"} = new RT::Links( $self->CurrentUser );
- if ( $self->CurrentUserHasRight('ShowTicket') ) {
+
+ #not sure what this ACL was supposed to do... but returning the
+ # bare (unlimited) RT::Links certainly seems wrong, it causes the
+ # $Ticket->Customers method during creation to return results for every
+ # ticket...
+ #if ( $self->CurrentUserHasRight('ShowTicket') ) {
+
# Maybe this ticket is a merged ticket
my $Tickets = new RT::Tickets( $self->CurrentUser );
# at least to myself
@@ -2221,7 +2568,7 @@ sub _Links {
$self->{"$field$type"}->Limit( FIELD => 'Type',
VALUE => $type )
if ($type);
- }
+ #}
}
return ( $self->{"$field$type"} );
}
@@ -2232,14 +2579,9 @@ sub _Links {
=head2 DeleteLink
-Delete a link. takes a paramhash of Base, Target, Type, Silent,
-SilentBase and SilentTarget. Either Base or Target must be null.
-The null value will be replaced with this ticket\'s id.
-
-If Silent is true then no transaction would be recorded, in other
-case you can control creation of transactions on both base and
-target with SilentBase and SilentTarget respectively. By default
-both transactions are created.
+Delete a link. takes a paramhash of Base, Target and Type.
+Either Base or Target must be null. The null value will
+be replaced with this ticket\'s id
=cut
@@ -2249,21 +2591,18 @@ sub DeleteLink {
Base => undef,
Target => undef,
Type => undef,
- Silent => undef,
- SilentBase => undef,
- SilentTarget => undef,
@_
);
unless ( $args{'Target'} || $args{'Base'} ) {
- $RT::Logger->error("Base or Target must be specified");
+ $RT::Logger->error("Base or Target must be specified\n");
return ( 0, $self->loc('Either base or target must be specified') );
}
#check acls
my $right = 0;
$right++ if $self->CurrentUserHasRight('ModifyTicket');
- if ( !$right && RT->Config->Get( 'StrictLinkACL' ) ) {
+ if ( !$right && $RT::StrictLinkACL ) {
return ( 0, $self->loc("Permission Denied") );
}
@@ -2274,55 +2613,57 @@ sub DeleteLink {
if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
$right++;
}
- if ( ( !RT->Config->Get( 'StrictLinkACL' ) && $right == 0 ) ||
- ( RT->Config->Get( 'StrictLinkACL' ) && $right < 2 ) )
+ if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+ ( $RT::StrictLinkACL && $right < 2 ) )
{
return ( 0, $self->loc("Permission Denied") );
}
my ($val, $Msg) = $self->SUPER::_DeleteLink(%args);
- return ( 0, $Msg ) unless $val;
- return ( $val, $Msg ) if $args{'Silent'};
+ if ( !$val ) {
+ $RT::Logger->debug("Couldn't find that link\n");
+ return ( 0, $Msg );
+ }
my ($direction, $remote_link);
if ( $args{'Base'} ) {
- $remote_link = $args{'Base'};
- $direction = 'Target';
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
}
elsif ( $args{'Target'} ) {
- $remote_link = $args{'Target'};
- $direction = 'Base';
- }
+ $remote_link = $args{'Target'};
+ $direction='Base';
+ }
- my $remote_uri = RT::URI->new( $self->CurrentUser );
- $remote_uri->FromURI( $remote_link );
+ if ( $args{'Silent'} ) {
+ return ( $val, $Msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
- unless ( $args{ 'Silent'. $direction } ) {
my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
Type => 'DeleteLink',
- Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
- OldValue => $remote_uri->URI || $remote_link,
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ OldValue => $remote_uri->URI || $remote_link,
TimeTaken => 0
);
- $RT::Logger->error("Couldn't create transaction: $Msg") unless $Trans;
- }
-
- if ( !$args{ 'Silent'. ( $direction eq 'Target'? 'Base': 'Target' ) } && $remote_uri->IsLocal ) {
- my $OtherObj = $remote_uri->Object;
- my ( $val, $Msg ) = $OtherObj->_NewTransaction(
- Type => 'DeleteLink',
- Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
- : $LINKDIRMAP{$args{'Type'}}->{Target},
- OldValue => $self->URI,
- ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'),
- TimeTaken => 0,
- );
- $RT::Logger->error("Couldn't create transaction: $Msg") unless $val;
- }
- return ( $val, $Msg );
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'DeleteLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ OldValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+
+ return ( $Trans, $Msg );
+ }
}
# }}}
@@ -2333,31 +2674,24 @@ sub DeleteLink {
Takes a paramhash of Type and one of Base or Target. Adds that link to this ticket.
-If Silent is true then no transaction would be recorded, in other
-case you can control creation of transactions on both base and
-target with SilentBase and SilentTarget respectively. By default
-both transactions are created.
-
=cut
sub AddLink {
my $self = shift;
- my %args = ( Target => '',
- Base => '',
- Type => '',
- Silent => undef,
- SilentBase => undef,
- SilentTarget => undef,
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
@_ );
unless ( $args{'Target'} || $args{'Base'} ) {
- $RT::Logger->error("Base or Target must be specified");
+ $RT::Logger->error("Base or Target must be specified\n");
return ( 0, $self->loc('Either base or target must be specified') );
}
my $right = 0;
$right++ if $self->CurrentUserHasRight('ModifyTicket');
- if ( !$right && RT->Config->Get( 'StrictLinkACL' ) ) {
+ if ( !$right && $RT::StrictLinkACL ) {
return ( 0, $self->loc("Permission Denied") );
}
@@ -2368,8 +2702,8 @@ sub AddLink {
if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
$right++;
}
- if ( ( !RT->Config->Get( 'StrictLinkACL' ) && $right == 0 ) ||
- ( RT->Config->Get( 'StrictLinkACL' ) && $right < 2 ) )
+ if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+ ( $RT::StrictLinkACL && $right < 2 ) )
{
return ( 0, $self->loc("Permission Denied") );
}
@@ -2387,8 +2721,8 @@ sub __GetTicketFromURI {
$uri_obj->FromURI( $args{'URI'} );
unless ( $uri_obj->Resolver && $uri_obj->Scheme ) {
- my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} );
- $RT::Logger->warning( $msg );
+ my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} );
+ $RT::Logger->warning( "$msg\n" );
return( 0, $msg );
}
my $obj = $uri_obj->Resolver->Object;
@@ -2406,17 +2740,14 @@ Private non-acled variant of AddLink so that links can be added during create.
sub _AddLink {
my $self = shift;
- my %args = ( Target => '',
- Base => '',
- Type => '',
- Silent => undef,
- SilentBase => undef,
- SilentTarget => undef,
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
@_ );
my ($val, $msg, $exist) = $self->SUPER::_AddLink(%args);
return ($val, $msg) if !$val || $exist;
- return ($val, $msg) if $args{'Silent'};
my ($direction, $remote_link);
if ( $args{'Target'} ) {
@@ -2427,33 +2758,34 @@ sub _AddLink {
$direction = 'Target';
}
- my $remote_uri = RT::URI->new( $self->CurrentUser );
- $remote_uri->FromURI( $remote_link );
-
- unless ( $args{ 'Silent'. $direction } ) {
- my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
- Type => 'AddLink',
- Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
- NewValue => $remote_uri->URI || $remote_link,
- TimeTaken => 0
- );
- $RT::Logger->error("Couldn't create transaction: $Msg") unless $Trans;
- }
-
- if ( !$args{ 'Silent'. ( $direction eq 'Target'? 'Base': 'Target' ) } && $remote_uri->IsLocal ) {
- my $OtherObj = $remote_uri->Object;
- my ( $val, $msg ) = $OtherObj->_NewTransaction(
- Type => 'AddLink',
- Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
- : $LINKDIRMAP{$args{'Type'}}->{Target},
- NewValue => $self->URI,
- ActivateScrips => !RT->Config->Get('LinkTransactionsRun1Scrip'),
- TimeTaken => 0,
- );
- $RT::Logger->error("Couldn't create transaction: $msg") unless $val;
+ # Don't write the transaction if we're doing this on create
+ if ( $args{'Silent'} ) {
+ return ( $val, $msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ #Write the transaction
+ my ( $Trans, $Msg, $TransObj ) =
+ $self->_NewTransaction(Type => 'AddLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ NewValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0 );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'AddLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ NewValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+ return ( $val, $Msg );
}
- return ( $val, $msg );
}
# }}}
@@ -2466,6 +2798,26 @@ sub _AddLink {
MergeInto take the id of the ticket to merge this ticket into.
+=begin testing
+
+my $t1 = RT::Ticket->new($RT::SystemUser);
+$t1->Create ( Subject => 'Merge test 1', Queue => 'general', Requestor => 'merge1@example.com');
+my $t1id = $t1->id;
+my $t2 = RT::Ticket->new($RT::SystemUser);
+$t2->Create ( Subject => 'Merge test 2', Queue => 'general', Requestor => 'merge2@example.com');
+my $t2id = $t2->id;
+my ($msg, $val) = $t1->MergeInto($t2->id);
+ok ($msg,$val);
+$t1 = RT::Ticket->new($RT::SystemUser);
+is ($t1->id, undef, "ok. we've got a blank ticket1");
+$t1->Load($t1id);
+
+is ($t1->id, $t2->id);
+
+is ($t1->Requestors->MembersObj->Count, 2);
+
+
+=end testing
=cut
@@ -2631,29 +2983,6 @@ sub MergeInto {
return ( 1, $self->loc("Merge Successful") );
}
-=head2 Merged
-
-Returns list of tickets' ids that's been merged into this ticket.
-
-=cut
-
-sub Merged {
- my $self = shift;
-
- my $mergees = new RT::Tickets( $self->CurrentUser );
- $mergees->Limit(
- FIELD => 'EffectiveId',
- OPERATOR => '=',
- VALUE => $self->Id,
- );
- $mergees->Limit(
- FIELD => 'id',
- OPERATOR => '!=',
- VALUE => $self->Id,
- );
- return map $_->id, @{ $mergees->ItemsArrayRef || [] };
-}
-
# }}}
# }}}
@@ -2710,6 +3039,28 @@ Takes two arguments:
and (optionally) the type of the SetOwner Transaction. It defaults
to 'Give'. 'Steal' is also a valid option.
+=begin testing
+
+my $root = RT::User->new($RT::SystemUser);
+$root->Load('root');
+ok ($root->Id, "Loaded the root user");
+my $t = RT::Ticket->new($RT::SystemUser);
+$t->Load(1);
+$t->SetOwner('root');
+is ($t->OwnerObj->Name, 'root' , "Root owns the ticket");
+$t->Steal();
+is ($t->OwnerObj->id, $RT::SystemUser->id , "SystemUser owns the ticket");
+my $txns = RT::Transactions->new($RT::SystemUser);
+$txns->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$txns->Limit(FIELD => 'ObjectId', VALUE => '1');
+$txns->Limit(FIELD => 'ObjectType', VALUE => 'RT::Ticket');
+$txns->Limit(FIELD => 'Type', OPERATOR => '!=', VALUE => 'EmailRecord');
+
+my $steal = $txns->First;
+ok($steal->OldValue == $root->Id , "Stolen from root");
+ok($steal->NewValue == $RT::SystemUser->Id , "Stolen by the systemuser");
+
+=end testing
=cut
@@ -2792,15 +3143,10 @@ sub SetOwner {
# Delete the owner in the owner group, then add a new one
# TODO: is this safe? it's not how we really want the API to work
# for most things, but it's fast.
- my ( $del_id, $del_msg );
- for my $owner (@{$self->OwnerGroup->MembersObj->ItemsArrayRef}) {
- ($del_id, $del_msg) = $owner->Delete();
- last unless ($del_id);
- }
-
+ my ( $del_id, $del_msg ) = $self->OwnerGroup->MembersObj->First->Delete();
unless ($del_id) {
$RT::Handle->Rollback();
- return ( 0, $self->loc("Could not change owner: [_1]", $del_msg) );
+ return ( 0, $self->loc("Could not change owner. ") . $del_msg );
}
my ( $add_id, $add_msg ) = $self->OwnerGroup->_AddMember(
@@ -2808,7 +3154,7 @@ sub SetOwner {
InsideTransaction => 1 );
unless ($add_id) {
$RT::Handle->Rollback();
- return ( 0, $self->loc("Could not change owner: [_1]", $add_msg ) );
+ return ( 0, $self->loc("Could not change owner. ") . $add_msg );
}
# We call set twice with slightly different arguments, so
@@ -2825,7 +3171,7 @@ sub SetOwner {
unless ($val) {
$RT::Handle->Rollback;
- return ( 0, $self->loc("Could not change owner: [_1]", $msg) );
+ return ( 0, $self->loc("Could not change owner. ") . $msg );
}
($val, $msg) = $self->_NewTransaction(
@@ -2942,6 +3288,25 @@ Set this ticket\'s status. STATUS can be one of: new, open, stalled, resolved, r
Alternatively, you can pass in a list of named parameters (Status => STATUS, Force => FORCE). If FORCE is true, ignore unresolved dependencies and force a status change.
+=begin testing
+
+my $tt = RT::Ticket->new($RT::SystemUser);
+my ($id, $tid, $msg)= $tt->Create(Queue => 'general',
+ Subject => 'test');
+ok($id, $msg);
+is($tt->Status, 'new', "New ticket is created as new");
+
+($id, $msg) = $tt->SetStatus('open');
+ok($id, $msg);
+like($msg, qr/open/i, "Status message is correct");
+($id, $msg) = $tt->SetStatus('resolved');
+ok($id, $msg);
+like($msg, qr/resolved/i, "Status message is correct");
+($id, $msg) = $tt->SetStatus('resolved');
+ok(!$id,$msg);
+
+
+=end testing
=cut
@@ -2951,10 +3316,10 @@ sub SetStatus {
my %args;
if (@_ == 1) {
- $args{Status} = shift;
+ $args{Status} = shift;
}
else {
- %args = (@_);
+ %args = (@_);
}
#Check ACL
@@ -2976,7 +3341,7 @@ sub SetStatus {
$now->SetToNow();
#If we're changing the status from new, record that we've started
- if ( $self->Status eq 'new' && $args{Status} ne 'new' ) {
+ if ( ( $self->Status =~ /new/ ) && ( $args{Status} ne 'new' ) ) {
#Set the Started time to "now"
$self->_Set( Field => 'Started',
@@ -3004,14 +3369,20 @@ sub SetStatus {
# }}}
-# {{{ sub Delete
+# {{{ sub Kill
-=head2 Delete
+=head2 Kill
Takes no arguments. Marks this ticket for garbage collection
=cut
+sub Kill {
+ my $self = shift;
+ $RT::Logger->crit("'Kill' is deprecated. use 'Delete' instead at (". join(":",caller).").");
+ return $self->Delete;
+}
+
sub Delete {
my $self = shift;
return ( $self->SetStatus('deleted') );
@@ -3083,7 +3454,7 @@ sub Resolve {
# }}}
-
+
# {{{ Actions + Routines dealing with transactions
# {{{ sub SetTold and _SetTold
@@ -3136,39 +3507,14 @@ sub _SetTold {
Value => $now->ISO ) );
}
-=head2 SeenUpTo
-
-
-=cut
-
-sub SeenUpTo {
- my $self = shift;
- my $uid = $self->CurrentUser->id;
- my $attr = $self->FirstAttribute( "User-". $uid ."-SeenUpTo" );
- return if $attr && $attr->Content gt $self->LastUpdated;
-
- my $txns = $self->Transactions;
- $txns->Limit( FIELD => 'Type', VALUE => 'Comment' );
- $txns->Limit( FIELD => 'Type', VALUE => 'Correspond' );
- $txns->Limit( FIELD => 'Creator', OPERATOR => '!=', VALUE => $uid );
- $txns->Limit(
- FIELD => 'Created',
- OPERATOR => '>',
- VALUE => $attr->Content
- ) if $attr;
- $txns->RowsPerPage(1);
- return $txns->First;
-}
-
# }}}
=head2 TransactionBatch
-Returns an array reference of all transactions created on this ticket during
-this ticket object's lifetime or since last application of a batch, or undef
-if there were none.
+ Returns an array reference of all transactions created on this ticket during
+ this ticket object's lifetime, or undef if there were none.
-Only works when the C<UseTransactionBatch> config option is set to true.
+ Only works when the $RT::UseTransactionBatch config variable is set to true.
=cut
@@ -3177,49 +3523,6 @@ sub TransactionBatch {
return $self->{_TransactionBatch};
}
-=head2 ApplyTransactionBatch
-
-Applies scrips on the current batch of transactions and shinks it. Usually
-batch is applied when object is destroyed, but in some cases it's too late.
-
-=cut
-
-sub ApplyTransactionBatch {
- my $self = shift;
-
- my $batch = $self->TransactionBatch;
- return unless $batch && @$batch;
-
- $self->_ApplyTransactionBatch;
-
- $self->{_TransactionBatch} = [];
-}
-
-sub _ApplyTransactionBatch {
- my $self = shift;
- my $batch = $self->TransactionBatch;
-
- my %seen;
- my $types = join ',', grep !$seen{$_}++, grep defined, map $_->Type, grep defined, @{$batch};
-
- require RT::Scrips;
- RT::Scrips->new($RT::SystemUser)->Apply(
- Stage => 'TransactionBatch',
- TicketObj => $self,
- TransactionObj => $batch->[0],
- Type => $types,
- );
-
- # Entry point of the rule system
- my $rules = RT::Ruleset->FindAllRules(
- Stage => 'TransactionBatch',
- TicketObj => $self,
- TransactionObj => $batch->[0],
- Type => $types,
- );
- RT::Ruleset->CommitRules($rules);
-}
-
sub DESTROY {
my $self = shift;
@@ -3233,10 +3536,16 @@ sub DESTROY {
# when an object's refcount is changed in its destructor.
return if $self->{_Destroyed}++;
- my $batch = $self->TransactionBatch;
- return unless $batch && @$batch;
+ my $batch = $self->TransactionBatch or return;
+ return unless @$batch;
- return $self->_ApplyTransactionBatch;
+ require RT::Scrips;
+ RT::Scrips->new($RT::SystemUser)->Apply(
+ Stage => 'TransactionBatch',
+ TicketObj => $self,
+ TransactionObj => $batch->[0],
+ Type => join(',', (map { $_->Type } @{$batch}) )
+ );
}
# }}}
@@ -3353,7 +3662,7 @@ sub _Value {
#if the field is public, return it.
if ( $self->_Accessible( $field, 'public' ) ) {
- #$RT::Logger->debug("Skipping ACL check for $field");
+ #$RT::Logger->debug("Skipping ACL check for $field\n");
return ( $self->SUPER::_Value($field) );
}
@@ -3412,10 +3721,13 @@ sub CurrentUserHasRight {
my $self = shift;
my $right = shift;
- return $self->CurrentUser->PrincipalObj->HasRight(
- Object => $self,
- Right => $right,
- )
+ return (
+ $self->HasRight(
+ Principal => $self->CurrentUser->UserObj(),
+ Right => "$right"
+ )
+ );
+
}
# }}}
@@ -3442,7 +3754,7 @@ sub HasRight {
unless ( ( defined $args{'Principal'} ) and ( ref( $args{'Principal'} ) ) )
{
- Carp::cluck("Principal attrib undefined for Ticket::HasRight");
+ Carp::cluck;
$RT::Logger->crit("Principal attrib undefined for Ticket::HasRight");
return(undef);
}
@@ -3499,13 +3811,11 @@ sub Transactions {
# if the user may not see comments do not return them
unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
$transactions->Limit(
- SUBCLAUSE => 'acl',
FIELD => 'Type',
OPERATOR => '!=',
VALUE => "Comment"
);
$transactions->Limit(
- SUBCLAUSE => 'acl',
FIELD => 'Type',
OPERATOR => '!=',
VALUE => "CommentEmailRecord",
@@ -3513,13 +3823,6 @@ sub Transactions {
);
}
- } else {
- $transactions->Limit(
- SUBCLAUSE => 'acl',
- FIELD => 'id',
- VALUE => 0,
- ENTRYAGGREGATOR => 'AND'
- );
}
return ($transactions);
@@ -3558,17 +3861,18 @@ sub CustomFieldValues {
my $self = shift;
my $field = shift;
- return $self->SUPER::CustomFieldValues( $field ) if !$field || $field =~ /^\d+$/;
+ return $self->SUPER::CustomFieldValues( $field )
+ if !$field || $field =~ /^\d+$/;
my $cf = RT::CustomField->new( $self->CurrentUser );
- $cf->SetContextObject( $self );
$cf->LoadByNameAndQueue( Name => $field, Queue => $self->Queue );
unless ( $cf->id ) {
$cf->LoadByNameAndQueue( Name => $field, Queue => 0 );
}
# If we didn't find a valid cfid, give up.
- return RT::ObjectCustomFieldValues->new( $self->CurrentUser ) unless $cf->id;
+ return RT::ObjectCustomFieldValues->new( $self->CurrentUser )
+ unless $cf->id;
return $self->SUPER::CustomFieldValues( $cf->id );
}
@@ -3590,23 +3894,6 @@ sub CustomFieldLookupType {
"RT::Queue-RT::Ticket";
}
-=head2 ACLEquivalenceObjects
-
-This method returns a list of objects for which a user's rights also apply
-to this ticket. Generally, this is only the ticket's queue, but some RT
-extensions may make other objects available too.
-
-This method is called from L<RT::Principal/HasRight>.
-
-=cut
-
-sub ACLEquivalenceObjects {
- my $self = shift;
- return $self->QueueObj;
-
-}
-
-
1;
=head1 AUTHOR
diff --git a/rt/lib/RT/Tickets.pm b/rt/lib/RT/Tickets.pm
index d0699e599..b6b349144 100755
--- a/rt/lib/RT/Tickets.pm
+++ b/rt/lib/RT/Tickets.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Ticket item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Transaction.pm b/rt/lib/RT/Transaction.pm
index 351e79d51..ca491a6c7 100755
--- a/rt/lib/RT/Transaction.pm
+++ b/rt/lib/RT/Transaction.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -70,6 +45,7 @@ RT::Transaction
package RT::Transaction;
use RT::Record;
+use RT::Ticket;
use vars qw( @ISA );
@@ -86,21 +62,18 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
- varchar(64) 'ObjectType'.
- int(11) 'ObjectId'.
+ int(11) 'EffectiveTicket'.
+ int(11) 'Ticket'.
int(11) 'TimeTaken'.
varchar(20) 'Type'.
varchar(40) 'Field'.
varchar(255) 'OldValue'.
varchar(255) 'NewValue'.
- varchar(255) 'ReferenceType'.
- int(11) 'OldReference'.
- int(11) 'NewReference'.
- varchar(255) 'Data'.
+ varchar(100) 'Data'.
=cut
@@ -110,30 +83,24 @@ Create takes a hash of values and creates a row in the database:
sub Create {
my $self = shift;
my %args = (
- ObjectType => '',
- ObjectId => '0',
+ EffectiveTicket => '0',
+ Ticket => '0',
TimeTaken => '0',
Type => '',
Field => '',
OldValue => '',
NewValue => '',
- ReferenceType => '',
- OldReference => '',
- NewReference => '',
Data => '',
@_);
$self->SUPER::Create(
- ObjectType => $args{'ObjectType'},
- ObjectId => $args{'ObjectId'},
+ EffectiveTicket => $args{'EffectiveTicket'},
+ Ticket => $args{'Ticket'},
TimeTaken => $args{'TimeTaken'},
Type => $args{'Type'},
Field => $args{'Field'},
OldValue => $args{'OldValue'},
NewValue => $args{'NewValue'},
- ReferenceType => $args{'ReferenceType'},
- OldReference => $args{'OldReference'},
- NewReference => $args{'NewReference'},
Data => $args{'Data'},
);
@@ -141,7 +108,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -150,50 +117,64 @@ Returns the current value of id.
=cut
-=head2 ObjectType
+=item EffectiveTicket
-Returns the current value of ObjectType.
-(In the database, ObjectType is stored as varchar(64).)
+Returns the current value of EffectiveTicket.
+(In the database, EffectiveTicket is stored as int(11).)
-=head2 SetObjectType VALUE
+=item SetEffectiveTicket VALUE
-Set ObjectType to VALUE.
+Set EffectiveTicket 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).)
+(In the database, EffectiveTicket will be stored as a int(11).)
=cut
-=head2 ObjectId
+=item Ticket
-Returns the current value of ObjectId.
-(In the database, ObjectId is stored as int(11).)
+Returns the current value of Ticket.
+(In the database, Ticket is stored as int(11).)
-=head2 SetObjectId VALUE
+=item SetTicket VALUE
-Set ObjectId to VALUE.
+Set Ticket 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).)
+(In the database, Ticket will be stored as a int(11).)
=cut
-=head2 TimeTaken
+=item TicketObj
+
+Returns the Ticket Object which has the id returned by Ticket
+
+
+=cut
+
+sub TicketObj {
+ my $self = shift;
+ my $Ticket = RT::Ticket->new($self->CurrentUser);
+ $Ticket->Load($self->__Value('Ticket'));
+ return($Ticket);
+}
+
+=item TimeTaken
Returns the current value of TimeTaken.
(In the database, TimeTaken is stored as int(11).)
-=head2 SetTimeTaken VALUE
+=item SetTimeTaken VALUE
Set TimeTaken to VALUE.
@@ -204,14 +185,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Type
+=item Type
Returns the current value of Type.
(In the database, Type is stored as varchar(20).)
-=head2 SetType VALUE
+=item SetType VALUE
Set Type to VALUE.
@@ -222,14 +203,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Field
+=item Field
Returns the current value of Field.
(In the database, Field is stored as varchar(40).)
-=head2 SetField VALUE
+=item SetField VALUE
Set Field to VALUE.
@@ -240,14 +221,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 OldValue
+=item OldValue
Returns the current value of OldValue.
(In the database, OldValue is stored as varchar(255).)
-=head2 SetOldValue VALUE
+=item SetOldValue VALUE
Set OldValue to VALUE.
@@ -258,14 +239,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 NewValue
+=item NewValue
Returns the current value of NewValue.
(In the database, NewValue is stored as varchar(255).)
-=head2 SetNewValue VALUE
+=item SetNewValue VALUE
Set NewValue to VALUE.
@@ -276,79 +257,25 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ReferenceType
-
-Returns the current value of ReferenceType.
-(In the database, ReferenceType is stored as varchar(255).)
-
-
-
-=head2 SetReferenceType VALUE
-
-
-Set ReferenceType to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, ReferenceType will be stored as a varchar(255).)
-
-
-=cut
-
-
-=head2 OldReference
-
-Returns the current value of OldReference.
-(In the database, OldReference is stored as int(11).)
-
-
-
-=head2 SetOldReference VALUE
-
-
-Set OldReference to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, OldReference will be stored as a int(11).)
-
-
-=cut
-
-
-=head2 NewReference
-
-Returns the current value of NewReference.
-(In the database, NewReference is stored as int(11).)
-
-
-
-=head2 SetNewReference VALUE
-
-
-Set NewReference to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, NewReference will be stored as a int(11).)
-
-
-=cut
-
-
-=head2 Data
+=item Data
Returns the current value of Data.
-(In the database, Data is stored as varchar(255).)
+(In the database, Data is stored as varchar(100).)
-=head2 SetData VALUE
+=item SetData VALUE
Set Data to VALUE.
Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, Data will be stored as a varchar(255).)
+(In the database, Data will be stored as a varchar(100).)
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -357,7 +284,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -367,37 +294,31 @@ Returns the current value of Created.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
- ObjectType =>
- {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
- ObjectId =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, type => 'int(11)', default => ''},
+ EffectiveTicket =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
+ Ticket =>
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
TimeTaken =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, write => 1, type => 'int(11)', default => '0'},
Type =>
- {read => 1, write => 1, sql_type => 12, length => 20, is_blob => 0, is_numeric => 0, type => 'varchar(20)', default => ''},
+ {read => 1, write => 1, type => 'varchar(20)', default => ''},
Field =>
- {read => 1, write => 1, sql_type => 12, length => 40, is_blob => 0, is_numeric => 0, type => 'varchar(40)', default => ''},
+ {read => 1, write => 1, type => 'varchar(40)', default => ''},
OldValue =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
NewValue =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
- ReferenceType =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
- OldReference =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
- NewReference =>
- {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, write => 1, type => 'varchar(255)', default => ''},
Data =>
- {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ {read => 1, write => 1, type => 'varchar(100)', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -429,7 +350,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Transaction_Overlay.pm b/rt/lib/RT/Transaction_Overlay.pm
index 96ccf51d4..1a5473b3c 100644
--- a/rt/lib/RT/Transaction_Overlay.pm
+++ b/rt/lib/RT/Transaction_Overlay.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::Transaction - RT\'s transaction object
@@ -65,6 +64,11 @@ It can have arbitrary MIME attachments.
=head1 METHODS
+=begin testing
+
+ok(require RT::Transaction);
+
+=end testing
=cut
@@ -78,11 +82,11 @@ use vars qw( %_BriefDescriptions $PreferredContentType );
use RT::Attachments;
use RT::Scrips;
-use RT::Ruleset;
use HTML::FormatText;
use HTML::TreeBuilder;
+
# {{{ sub Create
=head2 Create
@@ -172,21 +176,9 @@ sub Create {
Ticket => $args{'ObjectId'},
Transaction => $self->id,
);
-
- # Entry point of the rule system
- my $ticket = RT::Ticket->new($RT::SystemUser);
- $ticket->Load($args{'ObjectId'});
- my $rules = RT::Ruleset->FindAllRules(
- Stage => 'TransactionCreate',
- Type => $args{'Type'},
- TicketObj => $ticket,
- TransactionObj => $self,
- );
-
if ($args{'CommitScrips'} ) {
$RT::Logger->debug('About to commit scrips for transaction #' .$self->Id);
$self->{'scrips'}->Commit();
- RT::Ruleset->CommitRules($rules);
}
}
@@ -251,28 +243,26 @@ sub Delete {
=head2 Message
-Returns the L<RT::Attachments> object which contains the "top-level" object
-attachment for this transaction.
+ Returns the RT::Attachments Object which contains the "top-level"object
+ attachment for this transaction
=cut
sub Message {
- my $self = shift;
- # XXX: Where is ACL check?
+ my $self = shift;
- unless ( defined $self->{'message'} ) {
+ if ( !defined( $self->{'message'} ) ) {
- $self->{'message'} = RT::Attachments->new( $self->CurrentUser );
+ $self->{'message'} = new RT::Attachments( $self->CurrentUser );
$self->{'message'}->Limit(
FIELD => 'TransactionId',
VALUE => $self->Id
);
+
$self->{'message'}->ChildrenOf(0);
- } else {
- $self->{'message'}->GotoFirstItem;
}
- return $self->{'message'};
+ return ( $self->{'message'} );
}
# }}}
@@ -286,43 +276,43 @@ textual part (as defined in RT::I18N::IsTextualContentType). Otherwise,
returns undef.
Takes a paramhash. If the $args{'Quote'} parameter is set, wraps this message
-at $args{'Wrap'}. $args{'Wrap'} defaults to 70.
+at $args{'Wrap'}. $args{'Wrap'} defaults to $RT::MessageBoxWidth - 2 or 70.
-If $args{'Type'} is set to C<text/html>, this will return an HTML
-part of the message, if available. Otherwise it looks for a text/plain
-part. If $args{'Type'} is missing, it defaults to the value of
-C<$RT::Transaction::PreferredContentType>, if that's missing too,
-defaults to 'text/plain'.
+If $args{'Type'} is set to C<text/html>, plain texts are upgraded to HTML.
+Otherwise, HTML texts are downgraded to plain text. If $args{'Type'} is
+missing, it defaults to the value of C<$RT::Transaction::PreferredContentType>.
=cut
sub Content {
my $self = shift;
my %args = (
- Type => $PreferredContentType || 'text/plain',
+ Type => $PreferredContentType,
Quote => 0,
Wrap => 70,
+ Wrap => ( $RT::MessageBoxWidth || 72 ) - 2,
@_
);
my $content;
- if ( my $content_obj = $self->ContentObj( Type => $args{Type} ) ) {
- $content = $content_obj->Content ||'';
+ if (my $content_obj = $self->ContentObj) {
+ $content = $content_obj->Content;
- if ( lc $content_obj->ContentType eq 'text/html' ) {
+ if ($content_obj->ContentType =~ m{^text/html$}i) {
$content =~ s/<p>--\s+<br \/>.*?$//s if $args{'Quote'};
if ($args{Type} ne 'text/html') {
- my $tree = HTML::TreeBuilder->new_from_content( $content );
$content = HTML::FormatText->new(
leftmargin => 0,
rightmargin => 78,
- )->format( $tree);
- $tree->delete;
+ )->format(
+ HTML::TreeBuilder->new_from_content( $content )
+ );
}
- }
+ }
else {
$content =~ s/\n-- \n.*?$//s if $args{'Quote'};
+
if ($args{Type} eq 'text/html') {
# Extremely simple text->html converter
$content =~ s/&/&#38;/g;
@@ -343,10 +333,10 @@ sub Content {
# What's the longest line like?
my $max = 0;
foreach ( split ( /\n/, $content ) ) {
- $max = length if length > $max;
+ $max = length if ( length > $max );
}
- if ( $max > 76 ) {
+ if ( $max > $args{'Wrap'}+6 ) { # 76 ) {
require Text::Wrapper;
my $wrapper = new Text::Wrapper(
columns => $args{'Wrap'},
@@ -357,7 +347,7 @@ sub Content {
}
$content =~ s/^/> /gm;
- $content = $self->loc("On [_1], [_2] wrote:", $self->CreatedAsString, $self->CreatorObj->Name)
+ $content = $self->loc("On [_1], [_2] wrote:", $self->CreatedAsString(), $self->CreatorObj->Name())
. "\n$content\n\n";
}
@@ -366,26 +356,6 @@ sub Content {
# }}}
-
-=head2 Addresses
-
-Returns a hashref of addresses related to this transaction. See L<RT::Attachment/Addresses> for details.
-
-=cut
-
-sub Addresses {
- my $self = shift;
-
- if (my $attach = $self->Attachments->First) {
- return $attach->Addresses;
- }
- else {
- return {};
- }
-
-}
-
-
# {{{ ContentObj
=head2 ContentObj
@@ -396,13 +366,16 @@ Returns the RT::Attachment object which contains the content for this Transactio
sub ContentObj {
+
my $self = shift;
- my %args = ( Type => $PreferredContentType || 'text/plain',
- @_ );
- # If we don't have any content, return undef now.
+ # If we don\'t have any content, return undef now.
+ unless ( $self->Attachments->First ) {
+ return (undef);
+ }
+
# Get the set of toplevel attachments to this transaction.
- return undef unless my $Attachment = $self->Attachments->First;
+ my $Attachment = $self->Attachments->First();
# If it's a textual part, just return the body.
if ( RT::I18N::IsTextualContentType($Attachment->ContentType) ) {
@@ -412,23 +385,27 @@ sub ContentObj {
# If it's a multipart object, first try returning the first part with preferred
# MIME type ('text/plain' by default).
- elsif ( $Attachment->ContentType =~ '^multipart/' ) {
- my $plain_parts = $Attachment->Children;
- $plain_parts->ContentType( VALUE => $args{Type} );
- $plain_parts->LimitNotEmpty;
+ elsif ( $Attachment->ContentType() =~ '^multipart/' ) {
+ my $plain_parts = $Attachment->Children();
+ $plain_parts->ContentType( VALUE => ($PreferredContentType || 'text/plain') );
# If we actully found a part, return its content
- if ( my $first = $plain_parts->First ) {
- return $first;
+ if ( $plain_parts->First && $plain_parts->First->Content ne '' ) {
+ return ( $plain_parts->First );
}
+
# If that fails, return the first textual part which has some content.
- my $all_parts = $self->Attachments;
- while ( my $part = $all_parts->Next ) {
- next unless RT::I18N::IsTextualContentType($part->ContentType)
- && $part->Content;
- return $part;
+
+ else {
+ my $all_parts = $self->Attachments();
+ while ( my $part = $all_parts->Next ) {
+ if ( ( RT::I18N::IsTextualContentType($part->ContentType) ) and ( $part->Content() ne '' ) ) {
+ return ($part);
+ }
+ }
}
+
}
# We found no content. suck
@@ -448,8 +425,12 @@ Otherwise, returns null
sub Subject {
my $self = shift;
- return undef unless my $first = $self->Attachments->First;
- return $first->Subject;
+ if ( $self->Attachments->First ) {
+ return ( $self->Attachments->First->Subject );
+ }
+ else {
+ return (undef);
+ }
}
# }}}
@@ -458,7 +439,7 @@ sub Subject {
=head2 Attachments
-Returns all the RT::Attachment objects which are attached
+ Returns all the RT::Attachment objects which are attached
to this transaction. Takes an optional parameter, which is
a ContentType that Attachments should be restricted to.
@@ -467,28 +448,38 @@ a ContentType that Attachments should be restricted to.
sub Attachments {
my $self = shift;
- if ( $self->{'attachments'} ) {
- $self->{'attachments'}->GotoFirstItem;
- return $self->{'attachments'};
- }
+ unless ( $self->{'attachments'} ) {
+ $self->{'attachments'} = RT::Attachments->new( $self->CurrentUser );
- $self->{'attachments'} = RT::Attachments->new( $self->CurrentUser );
+ #If it's a comment, return an empty object if they don't have the right to see it
+ if ( $self->Type eq 'Comment' ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return ( $self->{'attachments'} );
+ }
+ }
- unless ( $self->CurrentUserCanSee ) {
- $self->{'attachments'}->Limit(FIELD => 'id', VALUE => '0');
- return $self->{'attachments'};
- }
+ #if they ain't got rights to see, return an empty object
+ elsif ($self->__Value('ObjectType') eq "RT::Ticket") {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return ( $self->{'attachments'} );
+ }
+ }
+
+ $self->{'attachments'}->Limit( FIELD => 'TransactionId',
+ VALUE => $self->Id );
- $self->{'attachments'}->Limit( FIELD => 'TransactionId', VALUE => $self->Id );
+ # Get the self->{'attachments'} in the order they're put into
+ # the database. Arguably, we should be returning a tree
+ # of self->{'attachments'}, not a set...but no current app seems to need
+ # it.
- # Get the self->{'attachments'} in the order they're put into
- # the database. Arguably, we should be returning a tree
- # of self->{'attachments'}, not a set...but no current app seems to need
- # it.
+ $self->{'attachments'}->OrderBy( ALIAS => 'main',
+ FIELD => 'id',
+ ORDER => 'asc' );
- $self->{'attachments'}->OrderBy( FIELD => 'id', ORDER => 'ASC' );
+ }
+ return ( $self->{'attachments'} );
- return $self->{'attachments'};
}
# }}}
@@ -505,70 +496,26 @@ sub _Attach {
my $self = shift;
my $MIMEObject = shift;
- unless ( defined $MIMEObject ) {
- $RT::Logger->error("We can't attach a mime object if you don't give us one.");
+ if ( !defined($MIMEObject) ) {
+ $RT::Logger->error(
+"$self _Attach: We can't attach a mime object if you don't give us one.\n"
+ );
return ( 0, $self->loc("[_1]: no attachment specified", $self) );
}
- my $Attachment = RT::Attachment->new( $self->CurrentUser );
+ my $Attachment = new RT::Attachment( $self->CurrentUser );
my ($id, $msg) = $Attachment->Create(
TransactionId => $self->Id,
Attachment => $MIMEObject
);
return ( $Attachment, $msg || $self->loc("Attachment created") );
+
}
# }}}
# }}}
-sub ContentAsMIME {
- my $self = shift;
-
- my $main_content = $self->ContentObj;
- my $entity = $main_content->ContentAsMIME;
-
- if ( $main_content->Parent ) {
- # main content is not top most entity, we shouldn't loose
- # From/To/Cc headers that are on a top part
- my $attachments = RT::Attachments->new( $self->CurrentUser );
- $attachments->Columns(qw(id Parent TransactionId Headers));
- $attachments->Limit( FIELD => 'TransactionId', VALUE => $self->id );
- $attachments->Limit( FIELD => 'Parent', VALUE => 0 );
- $attachments->Limit( FIELD => 'Parent', OPERATOR => 'IS', VALUE => 'NULL', QUOTEVALUE => 0 );
- $attachments->OrderBy( FIELD => 'id', ORDER => 'ASC' );
- my $tmp = $attachments->First;
- if ( $tmp && $tmp->id ne $main_content->id ) {
- $entity->make_multipart;
- $entity->head->add( split /:/, $_, 2 ) foreach $tmp->SplitHeaders;
- $entity->make_singlepart;
- }
- }
-
- my $attachments = RT::Attachments->new( $self->CurrentUser );
- $attachments->Limit( FIELD => 'TransactionId', VALUE => $self->id );
- $attachments->Limit(
- FIELD => 'id',
- OPERATOR => '!=',
- VALUE => $main_content->id,
- );
- $attachments->Limit(
- FIELD => 'ContentType',
- OPERATOR => 'NOT STARTSWITH',
- VALUE => 'multipart/',
- );
- $attachments->Limit(
- FIELD => 'Content',
- OPERATOR => '!=',
- VALUE => '',
- );
- while ( my $a = $attachments->Next ) {
- $entity->make_multipart unless $entity->is_multipart;
- $entity->add_part( $a->ContentAsMIME );
- }
- return $entity;
-}
-
# {{{ Routines dealing with Transaction Attributes
# {{{ sub Description
@@ -582,15 +529,28 @@ Returns a text string which describes this transaction
sub Description {
my $self = shift;
- unless ( $self->CurrentUserCanSee ) {
- return ( $self->loc("Permission Denied") );
+ #Check those ACLs
+ #If it's a comment or a comment email record,
+ # we need to be extra special careful
+
+ if ( $self->__Value('Type') =~ /^Comment/ ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return ( $self->loc("Permission Denied") );
+ }
}
- unless ( defined $self->Type ) {
+ #if they ain't got rights to see, don't let em
+ elsif ($self->__Value('ObjectType') eq "RT::Ticket") {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return ($self->loc("Permission Denied") );
+ }
+ }
+
+ if ( !defined( $self->Type ) ) {
return ( $self->loc("No transaction type specified"));
}
- return $self->loc("[_1] by [_2]", $self->BriefDescription , $self->CreatorObj->Name );
+ return ( $self->loc("[_1] by [_2]",$self->BriefDescription , $self->CreatorObj->Name ));
}
# }}}
@@ -606,13 +566,24 @@ Returns a text string which briefly describes this transaction
sub BriefDescription {
my $self = shift;
- unless ( $self->CurrentUserCanSee ) {
- return ( $self->loc("Permission Denied") );
+ #If it's a comment or a comment email record,
+ # we need to be extra special careful
+ if ( $self->__Value('Type') =~ /^Comment/ ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return ( $self->loc("Permission Denied") );
+ }
+ }
+
+ #if they ain't got rights to see, don't let em
+ elsif ( $self->__Value('ObjectType') eq "RT::Ticket" ) {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return ( $self->loc("Permission Denied") );
+ }
}
my $type = $self->Type; #cache this, rather than calling it 30 times
- unless ( defined $type ) {
+ if ( !defined($type) ) {
return $self->loc("No transaction type specified");
}
@@ -621,12 +592,6 @@ sub BriefDescription {
if ( $type eq 'Create' ) {
return ( $self->loc( "[_1] created", $obj_type ) );
}
- elsif ( $type eq 'Enabled' ) {
- return ( $self->loc( "[_1] enabled", $obj_type ) );
- }
- elsif ( $type eq 'Disabled' ) {
- return ( $self->loc( "[_1] disabled", $obj_type ) );
- }
elsif ( $type =~ /Status/ ) {
if ( $self->Field eq 'Status' ) {
if ( $self->NewValue eq 'deleted' ) {
@@ -700,10 +665,10 @@ sub BriefDescription {
$field = $cf->Name();
}
- if ( ! defined $self->OldValue || $self->OldValue eq '' ) {
+ if ( $self->OldValue eq '' ) {
return ( $self->loc("[_1] [_2] added", $field, $self->NewValue) );
}
- elsif ( !defined $self->NewValue || $self->NewValue eq '' ) {
+ elsif ( $self->NewValue eq '' ) {
return ( $self->loc("[_1] [_2] deleted", $field, $self->OldValue) );
}
@@ -832,21 +797,6 @@ sub BriefDescription {
return ( $self->Data );
}
},
- Told => sub {
- my $self = shift;
- if ( $self->Field eq 'Told' ) {
- my $t1 = new RT::Date($self->CurrentUser);
- $t1->Set(Format => 'ISO', Value => $self->NewValue);
- my $t2 = new RT::Date($self->CurrentUser);
- $t2->Set(Format => 'ISO', Value => $self->OldValue);
- return $self->loc( "[_1] changed from [_2] to [_3]", $self->loc($self->Field), $t2->AsString, $t1->AsString );
- }
- else {
- return $self->loc( "[_1] changed from [_2] to [_3]",
- $self->loc($self->Field),
- ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'" );
- }
- },
Set => sub {
my $self = shift;
if ( $self->Field eq 'Password' ) {
@@ -857,8 +807,7 @@ sub BriefDescription {
$q1->Load( $self->OldValue );
my $q2 = new RT::Queue( $self->CurrentUser );
$q2->Load( $self->NewValue );
- return $self->loc("[_1] changed from [_2] to [_3]",
- $self->loc($self->Field) , $q1->Name , $q2->Name);
+ return $self->loc("[_1] changed from [_2] to [_3]", $self->Field , $q1->Name , $q2->Name);
}
# Write the date/time change at local time:
@@ -867,12 +816,10 @@ sub BriefDescription {
$t1->Set(Format => 'ISO', Value => $self->NewValue);
my $t2 = new RT::Date($self->CurrentUser);
$t2->Set(Format => 'ISO', Value => $self->OldValue);
- return $self->loc( "[_1] changed from [_2] to [_3]", $self->loc($self->Field), $t2->AsString, $t1->AsString );
+ return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, $t2->AsString, $t1->AsString );
}
else {
- return $self->loc( "[_1] changed from [_2] to [_3]",
- $self->loc($self->Field),
- ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'" );
+ return $self->loc( "[_1] changed from [_2] to [_3]", $self->Field, ($self->OldValue? "'".$self->OldValue ."'" : $self->loc("(no value)")) , "'". $self->NewValue."'" );
}
},
PurgeTransaction => sub {
@@ -957,19 +904,52 @@ Returns its value as a string, if the user passes an ACL check
=cut
sub _Value {
+
my $self = shift;
my $field = shift;
#if the field is public, return it.
if ( $self->_Accessible( $field, 'public' ) ) {
- return $self->SUPER::_Value( $field );
+ return ( $self->__Value($field) );
+
+ }
+
+ #If it's a comment, we need to be extra special careful
+ if ( $self->__Value('Type') eq 'Comment' ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
+ return (undef);
+ }
+ }
+ elsif ( $self->__Value('Type') eq 'CommentEmailRecord' ) {
+ unless ( $self->CurrentUserHasRight('ShowTicketComments')
+ && $self->CurrentUserHasRight('ShowOutgoingEmail') ) {
+ return (undef);
+ }
+
}
+ elsif ( $self->__Value('Type') eq 'EmailRecord' ) {
+ unless ( $self->CurrentUserHasRight('ShowOutgoingEmail')) {
+ return (undef);
+ }
- unless ( $self->CurrentUserCanSee ) {
- return undef;
+ }
+ # Make sure the user can see the custom field before showing that it changed
+ elsif ( ( $self->__Value('Type') eq 'CustomField' ) && $self->__Value('Field') ) {
+ my $cf = RT::CustomField->new( $self->CurrentUser );
+ $cf->Load( $self->__Value('Field') );
+ return (undef) unless ( $cf->CurrentUserHasRight('SeeCustomField') );
+ }
+
+
+ #if they ain't got rights to see, don't let em
+ elsif ($self->__Value('ObjectType') eq "RT::Ticket") {
+ unless ( $self->CurrentUserHasRight('ShowTicket') ) {
+ return (undef);
+ }
}
- return $self->SUPER::_Value( $field );
+ return ( $self->__Value($field) );
+
}
# }}}
@@ -986,60 +966,14 @@ passed in here.
sub CurrentUserHasRight {
my $self = shift;
my $right = shift;
- return $self->CurrentUser->HasRight(
- Right => $right,
- Object => $self->Object
+ return (
+ $self->CurrentUser->HasRight(
+ Right => "$right",
+ Object => $self->TicketObj
+ )
);
}
-=head2 CurrentUserCanSee
-
-Returns true if current user has rights to see this particular transaction.
-
-This fact depends on type of the transaction, type of an object the transaction
-is attached to and may be other conditions, so this method is prefered over
-custom implementations.
-
-=cut
-
-sub CurrentUserCanSee {
- my $self = shift;
-
- # If it's a comment, we need to be extra special careful
- my $type = $self->__Value('Type');
- if ( $type eq 'Comment' ) {
- unless ( $self->CurrentUserHasRight('ShowTicketComments') ) {
- return 0;
- }
- }
- elsif ( $type eq 'CommentEmailRecord' ) {
- unless ( $self->CurrentUserHasRight('ShowTicketComments')
- && $self->CurrentUserHasRight('ShowOutgoingEmail') ) {
- return 0;
- }
- }
- elsif ( $type eq 'EmailRecord' ) {
- unless ( $self->CurrentUserHasRight('ShowOutgoingEmail') ) {
- return 0;
- }
- }
- # Make sure the user can see the custom field before showing that it changed
- elsif ( $type eq 'CustomField' and my $cf_id = $self->__Value('Field') ) {
- my $cf = RT::CustomField->new( $self->CurrentUser );
- $cf->SetContextObject( $self->Object );
- $cf->Load( $cf_id );
- return 0 unless $cf->CurrentUserHasRight('SeeCustomField');
- }
- #if they ain't got rights to see, don't let em
- elsif ( $self->__Value('ObjectType') eq "RT::Ticket" ) {
- unless ( $self->CurrentUserHasRight('ShowTicket') ) {
- return 0;
- }
- }
-
- return 1;
-}
-
# }}}
sub Ticket {
@@ -1084,7 +1018,7 @@ sub Object {
my $self = shift;
my $Object = $self->__Value('ObjectType')->new($self->CurrentUser);
$Object->Load($self->__Value('ObjectId'));
- return $Object;
+ return($Object);
}
sub FriendlyObjectType {
@@ -1114,6 +1048,7 @@ sub UpdateCustomFields {
# value "ARGSRef", which was a reference to a hash of arguments.
# This was insane. The next few lines of code preserve that API
# while giving us something saner.
+
# TODO: 3.6: DEPRECATE OLD API
@@ -1161,11 +1096,9 @@ sub CustomFieldValues {
if ( UNIVERSAL::can( $self->Object, 'QueueObj' ) ) {
- # XXX: $field could be undef when we want fetch values for all CFs
- # do we want to cover this situation somehow here?
unless ( defined $field && $field =~ /^\d+$/o ) {
my $CFs = RT::CustomFields->new( $self->CurrentUser );
- $CFs->Limit( FIELD => 'Name', VALUE => $field );
+ $CFs->Limit( FIELD => 'Name', VALUE => $field);
$CFs->LimitToLookupType($self->CustomFieldLookupType);
$CFs->LimitToGlobalOrObjectId($self->Object->QueueObj->id);
$field = $CFs->First->id if $CFs->First;
@@ -1191,54 +1124,7 @@ sub CustomFieldLookupType {
"RT::Queue-RT::Ticket-RT::Transaction";
}
-
-=head2 DeferredRecipients($freq, $include_sent )
-
-Takes the following arguments:
-
-=over
-
-=item * a string to indicate the frequency of digest delivery. Valid values are "daily", "weekly", or "susp".
-
-=item * an optional argument which, if true, will return addresses even if this notification has been marked as 'sent' for this transaction.
-
-=back
-
-Returns an array of users who should now receive the notification that
-was recorded in this transaction. Returns an empty array if there were
-no deferred users, or if $include_sent was not specified and the deferred
-notifications have been sent.
-
-=cut
-
-sub DeferredRecipients {
- my $self = shift;
- my $freq = shift;
- my $include_sent = @_? shift : 0;
-
- my $attr = $self->FirstAttribute('DeferredRecipients');
-
- return () unless ($attr);
-
- my $deferred = $attr->Content;
-
- return () unless ( ref($deferred) eq 'HASH' && exists $deferred->{$freq} );
-
- # Skip it.
-
- for my $user (keys %{$deferred->{$freq}}) {
- if ($deferred->{$freq}->{$user}->{_sent} && !$include_sent) {
- delete $deferred->{$freq}->{$user}
- }
- }
- # Now get our users. Easy.
-
- return keys %{ $deferred->{$freq} };
-}
-
-
-
-# Transactions don't change. by adding this cache config directive, we don't lose pathalogically on long tickets.
+# Transactions don't change. by adding this cache congif directiove, we don't lose pathalogically on long tickets.
sub _CacheConfig {
{
'cache_p' => 1,
@@ -1246,27 +1132,4 @@ sub _CacheConfig {
'cache_for_sec' => 6000,
}
}
-
-
-=head2 ACLEquivalenceObjects
-
-This method returns a list of objects for which a user's rights also apply
-to this Transaction.
-
-This currently only applies to Transaction Custom Fields on Tickets, so we return
-the Ticket's Queue and the Ticket.
-
-This method is called from L<RT::Principal/HasRight>.
-
-=cut
-
-sub ACLEquivalenceObjects {
- my $self = shift;
-
- return unless $self->ObjectType eq 'RT::Ticket';
- my $object = $self->Object;
- return $object,$object->QueueObj;
-
-}
-
1;
diff --git a/rt/lib/RT/Transactions.pm b/rt/lib/RT/Transactions.pm
index deae6ae27..23a475ac6 100755
--- a/rt/lib/RT/Transactions.pm
+++ b/rt/lib/RT/Transactions.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::Transaction item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/URI/freeside.pm b/rt/lib/RT/URI/freeside.pm
new file mode 100644
index 000000000..d73dbacad
--- /dev/null
+++ b/rt/lib/RT/URI/freeside.pm
@@ -0,0 +1,285 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 2004 Kristian Hoffmann <khoff@fire2wire.com>
+# Based on the original RT::URI::base and RT::URI::fsck_com_rt.
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@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
+package RT::URI::freeside;
+
+use RT::URI::base;
+use strict;
+use vars qw(@ISA $IntegrationType $URL);
+@ISA = qw/RT::URI::base/;
+
+
+=head1 NAME
+
+RT::URI::freeside
+
+=head1 DESCRIPTION
+
+URI handler for Freeside URIs. See http://www.freeside.biz/ for more
+information on Freeside.
+
+
+=head1 Public subroutines
+
+=over 4
+
+=item FreesideGetConfig CONFKEY
+
+Subroutine that returns the freeside's configuration value(s) for CONFKEY
+as a scalar or list.
+
+=cut
+
+sub FreesideGetConfig { return undef; }
+
+
+=item FreesideURL
+
+Returns the URL for freeside's web interface.
+
+=cut
+
+sub FreesideURL { return $URL; }
+
+
+=item FreesideVersion
+
+Returns a string describing the freeside version being used.
+
+=cut
+
+sub FreesideVersion { return undef; }
+
+
+=item smart_search
+
+A wrapper for the FS::cust_main::smart_search subroutine.
+
+=cut
+
+sub smart_search { return undef; }
+
+
+=item small_custview
+
+A wrapper for the FS::CGI::small_custview subroutine.
+
+=cut
+
+sub small_custview { return 'Freeside integration error!</A>'; }
+
+
+=back
+
+=head1 Private methods
+
+=over 4
+
+=item _FreesideGetRecord
+
+Method returns a hashref of the freeside record referenced in the URI.
+Must be called after ParseURI.
+
+=cut
+
+sub _FreesideGetRecord { return undef; }
+
+
+=item _FreesideURIPrefix
+
+Method that returns the URI prefix for freeside URIs.
+
+=cut
+
+sub _FreesideURIPrefix {
+
+ my $self = shift;
+ return($self->Scheme . '://freeside');
+
+}
+
+=item _FreesideURILabel
+
+Method that returns a short string describing the customer referenced
+in the URI.
+
+=cut
+
+sub _FreesideURILabel {
+
+ my $self = shift;
+
+ $RT::Logger->debug("Called _FreesideURILabel()");
+
+ return unless (exists($self->{'fstable'}) and
+ exists($self->{'fspkey'}));
+
+ my $label;
+ my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'});
+
+ if ($table ne 'cust_main') {
+ warn "FS::${table} not currently supported";
+ return;
+ }
+
+ my $rec = $self->_FreesideGetRecord();
+
+ if (ref($rec) eq 'HASH' and $table eq 'cust_main') {
+ my $name = $rec->{'last'} . ', ' . $rec->{'first'};
+ $name = $rec->{'company'} . " ($name)" if $rec->{'company'};
+ $label = "$pkey: $name";
+ } else {
+ $label = "$pkey: $table";
+ }
+
+ if ($label and !$@) {
+ return($label);
+ } else {
+ return;
+ }
+
+}
+
+=item _FreesideURILabelLong
+
+Method that returns a longer string describing the customer referenced
+in the URI.
+
+=cut
+
+sub _FreesideURILabelLong {
+
+ my $self = shift;
+
+ return $self->_FreesideURILabel();
+
+}
+
+=back
+
+=head1 Public methods
+
+=over 4
+
+=cut
+
+sub ParseURI {
+ my $self = shift;
+ my $uri = shift;
+ my ($table, $pkey);
+
+ my $uriprefix = $self->_FreesideURIPrefix;
+ if ($uri =~ /^$uriprefix\/(\w+)\/(\d+)$/) {
+ $table = $1;
+ $pkey = $2;
+ $self->{'scheme'} = $self->Scheme;
+ } else {
+ return(undef);
+ }
+
+ $self->{'uri'} = "${uriprefix}/${table}/${pkey}";
+ $self->{'fstable'} = $table;
+ $self->{'fspkey'} = $pkey;
+
+
+ my $url = $self->FreesideURL();
+
+ if ($url ne '') {
+ $self->{'href'} = "${url}/view/${table}.cgi?${pkey}";
+ } else {
+ $self->{'href'} = $self->{'uri'};
+ }
+
+ $self->{'uri'};
+
+}
+
+sub Scheme {
+ my $self = shift;
+ return('freeside');
+
+}
+
+sub HREF {
+ my $self = shift;
+ return($self->{'href'} || $self->{'uri'});
+}
+
+sub IsLocal {
+ my $self = shift;
+ return undef;
+}
+
+=item AsString
+
+Return a "pretty" string representing the URI object.
+
+This is meant to be used like this:
+
+ % $re = $uri->Resolver;
+ <A HREF="<% $re->HREF %>"><% $re->AsString %></A>
+
+=cut
+
+sub AsString {
+ my $self = shift;
+ my $prettystring;
+ if ($prettystring = $self->_FreesideURILabel) {
+ return $prettystring;
+ } else {
+ return $self->URI;
+ }
+}
+
+=item AsStringLong
+
+Return a longer (HTML) string representing the URI object.
+
+=cut
+
+sub AsStringLong {
+ my $self = shift;
+ my $prettystring;
+ if ($prettystring = $self->_FreesideURILabelLong || $self->_FreesideURILabel){
+ return $prettystring;
+ } else {
+ return $self->URI;
+ }
+}
+
+$IntegrationType ||= 'Internal';
+eval "require RT::URI::freeside::${RT::URI::freeside::IntegrationType}";
+warn $@ if $@;
+if ($@ &&
+ $@ !~ qr(^Can't locate RT/URI/freeside/${RT::URI::freeside::IntegrationType}.pm)) {
+ die $@;
+};
+
+=back
+
+=cut
+
+1;
diff --git a/rt/lib/RT/URI/freeside/Internal.pm b/rt/lib/RT/URI/freeside/Internal.pm
new file mode 100644
index 000000000..bd7c42ccf
--- /dev/null
+++ b/rt/lib/RT/URI/freeside/Internal.pm
@@ -0,0 +1,145 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 2004 Kristian Hoffmann <khoff@fire2wire.com>
+# Based on the original RT::URI::base and RT::URI::fsck_com_rt.
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@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);
+
+#use vars qw($conf);
+
+use FS;
+use FS::UID qw(dbh);
+use FS::CGI qw(popurl);
+use FS::UI::Web::small_custview qw(small_custview);
+use FS::Conf;
+use FS::Record qw(qsearchs qsearch dbdef);
+use FS::cust_main;
+use FS::cust_svc;
+
+=head1 NAME
+
+RT::URI::freeside::Internal
+
+=head1 DESCRIPTION
+
+Overlay for the RT::URI::freeside URI handler implementing the Internal integration type.
+
+See L<RT::URI::freeside> for public/private interface documentation.
+
+=cut
+
+
+
+sub _FreesideGetRecord {
+
+ my $self = shift;
+ my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'});
+
+ $RT::Logger->debug("Called _FreesideGetRecord()");
+
+ #eval "use FS::$table;";
+
+ my $dbdef = dbdef;
+ unless ($dbdef) {
+ $RT::Logger->error("Using Internal freeside integration type, ".
+ "but it doesn't look like we're running under ".
+ "freeside's Mason handler.");
+ return;
+ }
+
+ my $pkeyfield = $dbdef->table($table)->primary_key;
+ unless ($pkeyfield) {
+ $RT::Logger->error("No primary key for freeside table '$table'");
+ return;
+ }
+
+ my $fsrec = qsearchs($table, { $pkeyfield => $pkey });
+ unless ($fsrec) {
+ $RT::Logger->error("Record with '$pkeyfield' == '$pkey' does " .
+ "not exist in table $table");
+ return;
+ }
+
+ return { $fsrec->hash, '_object' => $fsrec };
+
+}
+
+sub FreesideVersion {
+
+ return $FS::VERSION;
+
+}
+
+sub FreesideGetConfig {
+
+ #$conf = new FS::Conf unless ref($conf);
+ my $conf = new FS::Conf;
+
+ return scalar($conf->config(@_));
+
+}
+
+sub smart_search { #Subroutine
+
+ return map { { $_->hash } } &FS::cust_main::smart_search(@_);
+
+}
+
+sub email_search { #Subroutine
+
+ return map { { $_->hash } } &FS::cust_main::email_search(@_);
+
+}
+
+sub small_custview {
+
+ return &FS::UI::Web::small_custview::small_custview(@_);
+
+}
+
+sub _FreesideURILabelLong {
+
+ my $self = shift;
+
+ my $table = $self->{'fstable'};
+
+ if ( $table eq 'cust_main' ) {
+
+ my $rec = $self->_FreesideGetRecord();
+ return small_custview( $rec->{'_object'},
+ scalar(FS::Conf->new->config('countrydefault')),
+ 1 #nobalance
+ );
+
+ } else {
+
+ return $self->_FreesideURILabel();
+
+ }
+
+}
+
+1;
diff --git a/rt/lib/RT/URI/freeside/XMLRPC.pm b/rt/lib/RT/URI/freeside/XMLRPC.pm
new file mode 100644
index 000000000..916c20d7b
--- /dev/null
+++ b/rt/lib/RT/URI/freeside/XMLRPC.pm
@@ -0,0 +1,122 @@
+# BEGIN LICENSE BLOCK
+#
+# Copyright (c) 2004 Kristian Hoffmann <khoff@fire2wire.com>
+# Based on the original RT::URI::base and RT::URI::fsck_com_rt.
+#
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@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);
+
+use vars qw($XMLRPC_URL $_FS_VERSION);
+
+use Frontier::Client;
+
+=head1 NAME
+
+RT::URI::freeside::XMLRPC
+
+=head1 DESCRIPTION
+
+Overlay for the RT::URI::freeside URI handler implementing the XMLRPC integration type.
+
+See L<RT::URI::freeside> for public/private interface documentation.
+
+=cut
+
+
+sub _XMLRPCRequest { #Subroutine
+
+ my $method = shift;
+ my @args = @_;
+
+ my $result;
+ eval {
+ my $server = new Frontier::Client ( url => $XMLRPC_URL );
+ $result = $server->call($method, @args);
+ };
+
+ if (not $@ and ref($result) eq 'ARRAY') {
+ return (scalar(@$result) == 1) ? @$result[0] : @$result;
+ } else {
+ $RT::Logger->debug("Freeside XMLRPC: " . $result || $@);
+ return ();
+ }
+
+}
+
+sub _FreesideGetRecord {
+
+ my $self = shift;
+ my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'});
+ my $record;
+
+ $RT::Logger->debug("Called XMLRPC::_FreesideGetRecord()");
+
+ #FIXME: Need a better way to get primary keys.
+ # Maybe create a method for it and cache them like version?
+ my %table_pkeys = (
+ cust_main => 'custnum',
+ );
+
+ my $method = 'Record.qsearchs';
+ my @args = ($table, { $table_pkeys{$table} => $pkey });
+ my ($record) = &_XMLRPCRequest($method, @args);
+
+ return $record;
+
+}
+
+
+sub FreesideGetConfig {
+
+ return _XMLRPCRequest('Conf.config', @_);
+
+}
+
+
+sub FreesideVersion {
+
+ return $_FS_VERSION if ($_FS_VERSION =~ /^\d+\.\d+\.\d+/);
+
+ $RT::Logger->debug("Requesting freeside version...");
+ ($_FS_VERSION) = &_XMLRPCRequest('version');
+ $RT::Logger->debug("Cached freeside version: ${_FS_VERSION}");
+
+ return $_FS_VERSION;
+
+}
+
+sub smart_search { #Subroutine
+
+ return _XMLRPCRequest('cust_main.smart_search', @_);
+
+}
+
+sub small_custview {
+
+ return _XMLRPCRequest('Web.UI.small_custview.small_custview', @_);
+
+}
+
+1;
diff --git a/rt/lib/RT/User.pm b/rt/lib/RT/User.pm
index 6919cbafa..cbc10f5b4 100755
--- a/rt/lib/RT/User.pm
+++ b/rt/lib/RT/User.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -86,7 +61,7 @@ sub _Init {
-=head2 Create PARAMHASH
+=item Create PARAMHASH
Create takes a hash of values and creates a row in the database:
@@ -195,7 +170,7 @@ sub Create {
-=head2 id
+=item id
Returns the current value of id.
(In the database, id is stored as int(11).)
@@ -204,14 +179,14 @@ Returns the current value of id.
=cut
-=head2 Name
+=item Name
Returns the current value of Name.
(In the database, Name is stored as varchar(200).)
-=head2 SetName VALUE
+=item SetName VALUE
Set Name to VALUE.
@@ -222,14 +197,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Password
+=item Password
Returns the current value of Password.
(In the database, Password is stored as varchar(40).)
-=head2 SetPassword VALUE
+=item SetPassword VALUE
Set Password to VALUE.
@@ -240,14 +215,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Comments
+=item Comments
Returns the current value of Comments.
(In the database, Comments is stored as blob.)
-=head2 SetComments VALUE
+=item SetComments VALUE
Set Comments to VALUE.
@@ -258,14 +233,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Signature
+=item Signature
Returns the current value of Signature.
(In the database, Signature is stored as blob.)
-=head2 SetSignature VALUE
+=item SetSignature VALUE
Set Signature to VALUE.
@@ -276,14 +251,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 EmailAddress
+=item EmailAddress
Returns the current value of EmailAddress.
(In the database, EmailAddress is stored as varchar(120).)
-=head2 SetEmailAddress VALUE
+=item SetEmailAddress VALUE
Set EmailAddress to VALUE.
@@ -294,14 +269,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 FreeformContactInfo
+=item FreeformContactInfo
Returns the current value of FreeformContactInfo.
(In the database, FreeformContactInfo is stored as blob.)
-=head2 SetFreeformContactInfo VALUE
+=item SetFreeformContactInfo VALUE
Set FreeformContactInfo to VALUE.
@@ -312,14 +287,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Organization
+=item Organization
Returns the current value of Organization.
(In the database, Organization is stored as varchar(200).)
-=head2 SetOrganization VALUE
+=item SetOrganization VALUE
Set Organization to VALUE.
@@ -330,14 +305,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 RealName
+=item RealName
Returns the current value of RealName.
(In the database, RealName is stored as varchar(120).)
-=head2 SetRealName VALUE
+=item SetRealName VALUE
Set RealName to VALUE.
@@ -348,14 +323,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 NickName
+=item NickName
Returns the current value of NickName.
(In the database, NickName is stored as varchar(16).)
-=head2 SetNickName VALUE
+=item SetNickName VALUE
Set NickName to VALUE.
@@ -366,14 +341,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Lang
+=item Lang
Returns the current value of Lang.
(In the database, Lang is stored as varchar(16).)
-=head2 SetLang VALUE
+=item SetLang VALUE
Set Lang to VALUE.
@@ -384,14 +359,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 EmailEncoding
+=item EmailEncoding
Returns the current value of EmailEncoding.
(In the database, EmailEncoding is stored as varchar(16).)
-=head2 SetEmailEncoding VALUE
+=item SetEmailEncoding VALUE
Set EmailEncoding to VALUE.
@@ -402,14 +377,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 WebEncoding
+=item WebEncoding
Returns the current value of WebEncoding.
(In the database, WebEncoding is stored as varchar(16).)
-=head2 SetWebEncoding VALUE
+=item SetWebEncoding VALUE
Set WebEncoding to VALUE.
@@ -420,14 +395,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ExternalContactInfoId
+=item ExternalContactInfoId
Returns the current value of ExternalContactInfoId.
(In the database, ExternalContactInfoId is stored as varchar(100).)
-=head2 SetExternalContactInfoId VALUE
+=item SetExternalContactInfoId VALUE
Set ExternalContactInfoId to VALUE.
@@ -438,14 +413,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ContactInfoSystem
+=item ContactInfoSystem
Returns the current value of ContactInfoSystem.
(In the database, ContactInfoSystem is stored as varchar(30).)
-=head2 SetContactInfoSystem VALUE
+=item SetContactInfoSystem VALUE
Set ContactInfoSystem to VALUE.
@@ -456,14 +431,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 ExternalAuthId
+=item ExternalAuthId
Returns the current value of ExternalAuthId.
(In the database, ExternalAuthId is stored as varchar(100).)
-=head2 SetExternalAuthId VALUE
+=item SetExternalAuthId VALUE
Set ExternalAuthId to VALUE.
@@ -474,14 +449,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 AuthSystem
+=item AuthSystem
Returns the current value of AuthSystem.
(In the database, AuthSystem is stored as varchar(30).)
-=head2 SetAuthSystem VALUE
+=item SetAuthSystem VALUE
Set AuthSystem to VALUE.
@@ -492,14 +467,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Gecos
+=item Gecos
Returns the current value of Gecos.
(In the database, Gecos is stored as varchar(16).)
-=head2 SetGecos VALUE
+=item SetGecos VALUE
Set Gecos to VALUE.
@@ -510,14 +485,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 HomePhone
+=item HomePhone
Returns the current value of HomePhone.
(In the database, HomePhone is stored as varchar(30).)
-=head2 SetHomePhone VALUE
+=item SetHomePhone VALUE
Set HomePhone to VALUE.
@@ -528,14 +503,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 WorkPhone
+=item WorkPhone
Returns the current value of WorkPhone.
(In the database, WorkPhone is stored as varchar(30).)
-=head2 SetWorkPhone VALUE
+=item SetWorkPhone VALUE
Set WorkPhone to VALUE.
@@ -546,14 +521,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 MobilePhone
+=item MobilePhone
Returns the current value of MobilePhone.
(In the database, MobilePhone is stored as varchar(30).)
-=head2 SetMobilePhone VALUE
+=item SetMobilePhone VALUE
Set MobilePhone to VALUE.
@@ -564,14 +539,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 PagerPhone
+=item PagerPhone
Returns the current value of PagerPhone.
(In the database, PagerPhone is stored as varchar(30).)
-=head2 SetPagerPhone VALUE
+=item SetPagerPhone VALUE
Set PagerPhone to VALUE.
@@ -582,14 +557,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Address1
+=item Address1
Returns the current value of Address1.
(In the database, Address1 is stored as varchar(200).)
-=head2 SetAddress1 VALUE
+=item SetAddress1 VALUE
Set Address1 to VALUE.
@@ -600,14 +575,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Address2
+=item Address2
Returns the current value of Address2.
(In the database, Address2 is stored as varchar(200).)
-=head2 SetAddress2 VALUE
+=item SetAddress2 VALUE
Set Address2 to VALUE.
@@ -618,14 +593,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 City
+=item City
Returns the current value of City.
(In the database, City is stored as varchar(100).)
-=head2 SetCity VALUE
+=item SetCity VALUE
Set City to VALUE.
@@ -636,14 +611,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 State
+=item State
Returns the current value of State.
(In the database, State is stored as varchar(100).)
-=head2 SetState VALUE
+=item SetState VALUE
Set State to VALUE.
@@ -654,14 +629,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Zip
+=item Zip
Returns the current value of Zip.
(In the database, Zip is stored as varchar(16).)
-=head2 SetZip VALUE
+=item SetZip VALUE
Set Zip to VALUE.
@@ -672,14 +647,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Country
+=item Country
Returns the current value of Country.
(In the database, Country is stored as varchar(50).)
-=head2 SetCountry VALUE
+=item SetCountry VALUE
Set Country to VALUE.
@@ -690,14 +665,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Timezone
+=item Timezone
Returns the current value of Timezone.
(In the database, Timezone is stored as varchar(50).)
-=head2 SetTimezone VALUE
+=item SetTimezone VALUE
Set Timezone to VALUE.
@@ -708,14 +683,14 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 PGPKey
+=item PGPKey
Returns the current value of PGPKey.
(In the database, PGPKey is stored as text.)
-=head2 SetPGPKey VALUE
+=item SetPGPKey VALUE
Set PGPKey to VALUE.
@@ -726,7 +701,7 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
=cut
-=head2 Creator
+=item Creator
Returns the current value of Creator.
(In the database, Creator is stored as int(11).)
@@ -735,7 +710,7 @@ Returns the current value of Creator.
=cut
-=head2 Created
+=item Created
Returns the current value of Created.
(In the database, Created is stored as datetime.)
@@ -744,7 +719,7 @@ Returns the current value of Created.
=cut
-=head2 LastUpdatedBy
+=item LastUpdatedBy
Returns the current value of LastUpdatedBy.
(In the database, LastUpdatedBy is stored as int(11).)
@@ -753,7 +728,7 @@ Returns the current value of LastUpdatedBy.
=cut
-=head2 LastUpdated
+=item LastUpdated
Returns the current value of LastUpdated.
(In the database, LastUpdated is stored as datetime.)
@@ -763,77 +738,77 @@ Returns the current value of LastUpdated.
-sub _CoreAccessible {
+sub _ClassAccessible {
{
id =>
- {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ {read => 1, type => 'int(11)', default => ''},
Name =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Password =>
- {read => 1, write => 1, sql_type => 12, length => 40, is_blob => 0, is_numeric => 0, type => 'varchar(40)', default => ''},
+ {read => 1, write => 1, type => 'varchar(40)', default => ''},
Comments =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ {read => 1, write => 1, type => 'blob', default => ''},
Signature =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ {read => 1, write => 1, type => 'blob', default => ''},
EmailAddress =>
- {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ {read => 1, write => 1, type => 'varchar(120)', default => ''},
FreeformContactInfo =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ {read => 1, write => 1, type => 'blob', default => ''},
Organization =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
RealName =>
- {read => 1, write => 1, sql_type => 12, length => 120, is_blob => 0, is_numeric => 0, type => 'varchar(120)', default => ''},
+ {read => 1, write => 1, type => 'varchar(120)', default => ''},
NickName =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
Lang =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
EmailEncoding =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
WebEncoding =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
ExternalContactInfoId =>
- {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ {read => 1, write => 1, type => 'varchar(100)', default => ''},
ContactInfoSystem =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
ExternalAuthId =>
- {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ {read => 1, write => 1, type => 'varchar(100)', default => ''},
AuthSystem =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
Gecos =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
HomePhone =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
WorkPhone =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
MobilePhone =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
PagerPhone =>
- {read => 1, write => 1, sql_type => 12, length => 30, is_blob => 0, is_numeric => 0, type => 'varchar(30)', default => ''},
+ {read => 1, write => 1, type => 'varchar(30)', default => ''},
Address1 =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
Address2 =>
- {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+ {read => 1, write => 1, type => 'varchar(200)', default => ''},
City =>
- {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ {read => 1, write => 1, type => 'varchar(100)', default => ''},
State =>
- {read => 1, write => 1, sql_type => 12, length => 100, is_blob => 0, is_numeric => 0, type => 'varchar(100)', default => ''},
+ {read => 1, write => 1, type => 'varchar(100)', default => ''},
Zip =>
- {read => 1, write => 1, sql_type => 12, length => 16, is_blob => 0, is_numeric => 0, type => 'varchar(16)', default => ''},
+ {read => 1, write => 1, type => 'varchar(16)', default => ''},
Country =>
- {read => 1, write => 1, sql_type => 12, length => 50, is_blob => 0, is_numeric => 0, type => 'varchar(50)', default => ''},
+ {read => 1, write => 1, type => 'varchar(50)', default => ''},
Timezone =>
- {read => 1, write => 1, sql_type => 12, length => 50, is_blob => 0, is_numeric => 0, type => 'varchar(50)', default => ''},
+ {read => 1, write => 1, type => 'varchar(50)', default => ''},
PGPKey =>
- {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'text', default => ''},
+ {read => 1, write => 1, type => 'text', default => ''},
Creator =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
Created =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
LastUpdatedBy =>
- {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ {read => 1, auto => 1, type => 'int(11)', default => '0'},
LastUpdated =>
- {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ {read => 1, auto => 1, type => 'datetime', default => ''},
}
};
@@ -865,7 +840,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/User_Overlay.pm b/rt/lib/RT/User_Overlay.pm
index db3964cd3..73c3bdd5b 100644
--- a/rt/lib/RT/User_Overlay.pm
+++ b/rt/lib/RT/User_Overlay.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::User - RT User object
@@ -59,6 +58,11 @@
=head1 METHODS
+=begin testing
+
+ok(require RT::User);
+
+=end testing
=cut
@@ -69,12 +73,19 @@ package RT::User;
use strict;
no warnings qw(redefine);
+use vars qw(%_USERS_KEY_CACHE);
+
+%_USERS_KEY_CACHE = ();
+
use Digest::MD5;
use RT::Principals;
use RT::ACE;
use RT::Interface::Email;
use Encode;
+# {{{ sub _Accessible
+
+
sub _OverlayAccessible {
{
@@ -99,9 +110,62 @@ sub _OverlayAccessible {
+# }}}
+
+# {{{ sub Create
+
=head2 Create { PARAMHASH }
+=begin testing
+
+# Make sure we can create a user
+
+my $u1 = RT::User->new($RT::SystemUser);
+is(ref($u1), 'RT::User');
+my ($id, $msg) = $u1->Create(Name => 'CreateTest1'.$$, EmailAddress => $$.'create-test-1@example.com');
+ok ($id, "Creating user CreateTest1 - " . $msg );
+
+# Make sure we can't create a second user with the same name
+my $u2 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u2->Create(Name => 'CreateTest1'.$$, EmailAddress => $$.'create-test-2@example.com');
+ok (!$id, $msg);
+
+
+# Make sure we can't create a second user with the same EmailAddress address
+my $u3 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u3->Create(Name => 'CreateTest2'.$$, EmailAddress => $$.'create-test-1@example.com');
+ok (!$id, $msg);
+
+# Make sure we can create a user with no EmailAddress address
+my $u4 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u4->Create(Name => 'CreateTest3'.$$);
+ok ($id, $msg);
+
+# make sure we can create a second user with no EmailAddress address
+my $u5 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u5->Create(Name => 'CreateTest4'.$$);
+ok ($id, $msg);
+
+# make sure we can create a user with a blank EmailAddress address
+my $u6 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u6->Create(Name => 'CreateTest6'.$$, EmailAddress => '');
+ok ($id, $msg);
+# make sure we can create a second user with a blankEmailAddress address
+my $u7 = RT::User->new($RT::SystemUser);
+($id, $msg) = $u7->Create(Name => 'CreateTest7'.$$, EmailAddress => '');
+ok ($id, $msg);
+
+# Can we change the email address away from from "";
+($id,$msg) = $u7->SetEmailAddress('foo@bar'.$$);
+ok ($id, $msg);
+# can we change the address back to "";
+($id,$msg) = $u7->SetEmailAddress('');
+ok ($id, $msg);
+is ($u7->EmailAddress, '');
+
+
+=end testing
=cut
@@ -121,7 +185,7 @@ sub Create {
#Check the ACL
unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
- return ( 0, $self->loc('Permission Denied') );
+ return ( 0, $self->loc('No permission to create users') );
}
@@ -136,7 +200,9 @@ sub Create {
- my $privileged = delete $args{'Privileged'};
+ # Privileged is no longer a column in users
+ my $privileged = $args{'Privileged'};
+ delete $args{'Privileged'};
if ($args{'CryptedPassword'} ) {
@@ -146,8 +212,8 @@ sub Create {
elsif ( !$args{'Password'} ) {
$args{'Password'} = '*NO-PASSWORD*';
}
- elsif ( length( $args{'Password'} ) < RT->Config->Get('MinimumPasswordLength') ) {
- return ( 0, $self->loc("Password needs to be at least [_1] characters long",RT->Config->Get('MinimumPasswordLength')) );
+ elsif ( length( $args{'Password'} ) < $RT::MinimumPasswordLength ) {
+ return ( 0, $self->loc("Password needs to be at least [_1] characters long",$RT::MinimumPasswordLength) );
}
else {
@@ -157,6 +223,8 @@ sub Create {
#TODO Specify some sensible defaults.
unless ( $args{'Name'} ) {
+ use Data::Dumper;
+ $RT::Logger->crit(Dumper \%args);
return ( 0, $self->loc("Must specify 'Name' attribute") );
}
@@ -166,8 +234,8 @@ sub Create {
$TempUser->Load( $args{'Name'} );
return ( 0, $self->loc('Name in use') ) if ( $TempUser->Id );
- my ($val, $message) = $self->ValidateEmailAddress( $args{'EmailAddress'} );
- return (0, $message) unless ( $val );
+ return ( 0, $self->loc('Email address in use') )
+ unless ( $self->ValidateEmailAddress( $args{'EmailAddress'} ) );
}
else {
$RT::Logger->warning( "$self couldn't check for pre-existing users");
@@ -256,7 +324,7 @@ sub Create {
if ( $record_transaction ) {
- $self->_NewTransaction( Type => "Create" );
+ $self->_NewTransaction( Type => "Create" );
}
$RT::Handle->Commit;
@@ -264,6 +332,12 @@ sub Create {
return ( $id, $self->loc('User created') );
}
+# }}}
+
+
+
+# {{{ SetPrivileged
+
=head2 SetPrivileged BOOL
If passed a true value, makes this user a member of the "Privileged" PseudoGroup.
@@ -271,6 +345,20 @@ Otherwise, makes this user a member of the "Unprivileged" pseudogroup.
Returns a standard RT tuple of (val, msg);
+=begin testing
+
+
+ok(my $user = RT::User->new($RT::SystemUser));
+ok($user->Load('root'), "Loaded user 'root'");
+ok($user->Privileged, "User 'root' is privileged");
+ok(my ($v,$m) = $user->SetPrivileged(0));
+ok ($v ==1, "Set unprivileged suceeded ($m)");
+ok(!$user->Privileged, "User 'root' is no longer privileged");
+ok(my ($v2,$m2) = $user->SetPrivileged(1));
+ok ($v2 ==1, "Set privileged suceeded ($m2");
+ok($user->Privileged, "User 'root' is privileged again");
+
+=end testing
=cut
@@ -282,9 +370,9 @@ sub SetPrivileged {
unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
return ( 0, $self->loc('Permission Denied') );
}
-
my $priv = RT::Group->new($self->CurrentUser);
$priv->LoadSystemInternalGroup('Privileged');
+
unless ($priv->Id) {
$RT::Logger->crit("Could not find Privileged pseudogroup");
return(0,$self->loc("Failed to find 'Privileged' users pseudogroup."));
@@ -297,14 +385,13 @@ sub SetPrivileged {
return(0,$self->loc("Failed to find 'Unprivileged' users pseudogroup"));
}
- my $principal = $self->PrincipalId;
if ($val) {
- if ($priv->HasMember($principal)) {
+ if ($priv->HasMember($self->PrincipalObj)) {
#$RT::Logger->debug("That user is already privileged");
return (0,$self->loc("That user is already privileged"));
}
- if ($unpriv->HasMember($principal)) {
- $unpriv->_DeleteMember($principal);
+ if ($unpriv->HasMember($self->PrincipalObj)) {
+ $unpriv->_DeleteMember($self->PrincipalId);
} else {
# if we had layered transactions, life would be good
# sadly, we have to just go ahead, even if something
@@ -312,7 +399,7 @@ sub SetPrivileged {
$RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
"unprivileged. something is drastically wrong.");
}
- my ($status, $msg) = $priv->_AddMember( InsideTransaction => 1, PrincipalId => $principal);
+ my ($status, $msg) = $priv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
if ($status) {
return (1, $self->loc("That user is now privileged"));
} else {
@@ -320,12 +407,12 @@ sub SetPrivileged {
}
}
else {
- if ($unpriv->HasMember($principal)) {
+ if ($unpriv->HasMember($self->PrincipalObj)) {
#$RT::Logger->debug("That user is already unprivileged");
return (0,$self->loc("That user is already unprivileged"));
}
- if ($priv->HasMember($principal)) {
- $priv->_DeleteMember( $principal );
+ if ($priv->HasMember($self->PrincipalObj)) {
+ $priv->_DeleteMember( $self->PrincipalId);
} else {
# if we had layered transactions, life would be good
# sadly, we have to just go ahead, even if something
@@ -333,7 +420,7 @@ sub SetPrivileged {
$RT::Logger->crit("User ".$self->Id." is neither privileged nor ".
"unprivileged. something is drastically wrong.");
}
- my ($status, $msg) = $unpriv->_AddMember( InsideTransaction => 1, PrincipalId => $principal);
+ my ($status, $msg) = $unpriv->_AddMember( InsideTransaction => 1, PrincipalId => $self->PrincipalId);
if ($status) {
return (1, $self->loc("That user is now unprivileged"));
} else {
@@ -342,6 +429,10 @@ sub SetPrivileged {
}
}
+# }}}
+
+# {{{ Privileged
+
=head2 Privileged
Returns true if this user is privileged. Returns undef otherwise.
@@ -352,7 +443,7 @@ sub Privileged {
my $self = shift;
my $priv = RT::Group->new($self->CurrentUser);
$priv->LoadSystemInternalGroup('Privileged');
- if ( $priv->HasMember( $self->PrincipalId ) ) {
+ if ($priv->HasMember($self->PrincipalObj)) {
return(1);
}
else {
@@ -360,6 +451,10 @@ sub Privileged {
}
}
+# }}}
+
+# {{{ sub _BootstrapCreate
+
#create a user without validating _any_ data.
#To be used only on database init.
@@ -409,6 +504,10 @@ sub _BootstrapCreate {
return ( $id, 'User created' );
}
+# }}}
+
+# {{{ sub Delete
+
sub Delete {
my $self = shift;
@@ -416,35 +515,40 @@ sub Delete {
}
+# }}}
+
+# {{{ sub Load
+
=head2 Load
Load a user object from the database. Takes a single argument.
-If the argument is numerical, load by the column 'id'. If a user
-object or its subclass passed then loads the same user by id.
-Otherwise, load by the "Name" column which is the user's textual
-username.
+If the argument is numerical, load by the column 'id'. Otherwise, load by
+the "Name" column which is the user's textual username.
=cut
sub Load {
- my $self = shift;
+ my $self = shift;
my $identifier = shift || return undef;
+ #if it's an int, load by id. otherwise, load by name.
if ( $identifier !~ /\D/ ) {
- return $self->SUPER::LoadById( $identifier );
- }
- elsif ( UNIVERSAL::isa( $identifier, 'RT::User' ) ) {
- return $self->SUPER::LoadById( $identifier->Id );
+ $self->SUPER::LoadById($identifier);
}
else {
- return $self->LoadByCol( "Name", $identifier );
+ $self->LoadByCol( "Name", $identifier );
}
}
+# }}}
+
+# {{{ sub LoadByEmail
+
=head2 LoadByEmail
Tries to load this user object from the database by the user's email address.
+
=cut
sub LoadByEmail {
@@ -458,15 +562,18 @@ sub LoadByEmail {
$address = $self->CanonicalizeEmailAddress($address);
- #$RT::Logger->debug("Trying to load an email address: $address");
+ #$RT::Logger->debug("Trying to load an email address: $address\n");
return $self->LoadByCol( "EmailAddress", $address );
}
+# }}}
+
+# {{{ LoadOrCreateByEmail
+
=head2 LoadOrCreateByEmail ADDRESS
Attempts to find a user who has the provided email address. If that fails, creates an unprivileged user with
-the provided email address and loads them. Address can be provided either as L<Email::Address> object
-or string which is parsed using the module.
+the provided email address. and loads them.
Returns a tuple of the user's id and a status message.
0 will be returned in place of the user's id in case of failure.
@@ -477,45 +584,54 @@ sub LoadOrCreateByEmail {
my $self = shift;
my $email = shift;
- my ($message, $name);
- if ( UNIVERSAL::isa( $email => 'Email::Address' ) ) {
- ($email, $name) = ($email->address, $email->phrase);
- } else {
- ($email, $name) = RT::Interface::Email::ParseAddressFromHeader( $email );
- }
-
- $self->LoadByEmail( $email );
- $self->Load( $email ) unless $self->Id;
- $message = $self->loc('User loaded');
-
- unless( $self->Id ) {
- my $val;
- ($val, $message) = $self->Create(
- Name => $email,
- EmailAddress => $email,
- RealName => $name,
- Privileged => 0,
- Comments => 'Autocreated when added as a watcher',
- );
- unless ( $val ) {
- # Deal with the race condition of two account creations at once
- $self->LoadByEmail( $email );
- unless ( $self->Id ) {
- sleep 5;
- $self->LoadByEmail( $email );
- }
- if ( $self->Id ) {
- $RT::Logger->error("Recovered from creation failure due to race condition");
- $message = $self->loc("User loaded");
- }
- else {
- $RT::Logger->crit("Failed to create user ". $email .": " .$message);
+ my ($val, $message);
+
+ my ( $Address, $Name ) =
+ RT::Interface::Email::ParseAddressFromHeader($email);
+ $email = $Address;
+
+ $self->LoadByEmail($email);
+ $message = $self->loc('User loaded');
+ unless ($self->Id) {
+ $self->Load($email);
+ }
+ unless($self->Id) {
+ ( $val, $message ) = $self->Create(
+ Name => $email,
+ EmailAddress => $email,
+ RealName => $Name,
+ Privileged => 0,
+ Comments => 'Autocreated when added as a watcher');
+ unless ($val) {
+ # Deal with the race condition of two account creations at once
+ $self->LoadByEmail($email);
+ unless ($self->Id) {
+ sleep 5;
+ $self->LoadByEmail($email);
+ }
+ if ($self->Id) {
+ $RT::Logger->error("Recovered from creation failure due to race condition");
+ $message = $self->loc("User loaded");
+ }
+ else {
+ $RT::Logger->crit("Failed to create user ".$email .": " .$message);
+ }
}
}
+
+ if ($self->Id) {
+ return($self->Id, $message);
+ }
+ else {
+ return(0, $message);
+ }
+
+
}
- return (0, $message) unless $self->id;
- return ($self->Id, $message);
-}
+
+# }}}
+
+# {{{ sub ValidateEmailAddress
=head2 ValidateEmailAddress ADDRESS
@@ -531,86 +647,24 @@ sub ValidateEmailAddress {
# if the email address is null, it's always valid
return (1) if ( !$Value || $Value eq "" );
- if ( RT->Config->Get('ValidateUserEmailAddresses') ) {
- # We only allow one valid email address
- my @addresses = Email::Address->parse($Value);
- return ( 0, $self->loc('Invalid syntax for email address') ) unless ( ( scalar (@addresses) == 1 ) && ( $addresses[0]->address ) );
- }
-
-
my $TempUser = RT::User->new($RT::SystemUser);
$TempUser->LoadByEmail($Value);
- if ( $TempUser->id && ( !$self->id || $TempUser->id != $self->id ) )
+ if ( $TempUser->id && ( $TempUser->id != $self->id ) )
{ # if we found a user with that address
# it's invalid to set this user's address to it
- return ( 0, $self->loc('Email address in use') );
+ return (undef);
}
else { #it's a valid email address
return (1);
}
}
-=head2 SetEmailAddress
-
-Check to make sure someone else isn't using this email address already
-so that a better email address can be returned
-
-=cut
-
-sub SetEmailAddress {
- my $self = shift;
- my $Value = shift;
-
- my ($val, $message) = $self->ValidateEmailAddress( $Value );
- if ( $val ) {
- return $self->_Set( Field => 'EmailAddress', Value => $Value );
- } else {
- return ( 0, $message )
- }
-
-}
-
-=head2 EmailFrequency
-
-Takes optional Ticket argument in paramhash. Returns 'no email',
-'squelched', 'daily', 'weekly' or empty string depending on
-user preferences.
-
-=over 4
-
-=item 'no email' - user has no email, so can not recieve notifications.
+# }}}
-=item 'squelched' - returned only when Ticket argument is provided and
-notifications to the user has been supressed for this ticket.
+# {{{ sub CanonicalizeEmailAddress
-=item 'daily' - retruned when user recieve daily messages digest instead
-of immediate delivery.
-=item 'weekly' - previous, but weekly.
-
-=item empty string returned otherwise.
-
-=back
-
-=cut
-
-sub EmailFrequency {
- my $self = shift;
- my %args = (
- Ticket => undef,
- @_
- );
- return '' unless $self->id && $self->id != $RT::Nobody->id
- && $self->id != $RT::SystemUser->id;
- return 'no email' unless my $email = $self->EmailAddress;
- return 'squelched' if $args{'Ticket'} &&
- grep lc $email eq lc $_->Content, $args{'Ticket'}->SquelchMailTo;
- my $frequency = RT->Config->Get( 'EmailFrequency', $self ) || '';
- return 'daily' if $frequency =~ /daily/i;
- return 'weekly' if $frequency =~ /weekly/i;
- return '';
-}
=head2 CanonicalizeEmailAddress ADDRESS
@@ -628,14 +682,19 @@ sub CanonicalizeEmailAddress {
# Example: the following rule would treat all email
# coming from a subdomain as coming from second level domain
# foo.com
- if ( my $match = RT->Config->Get('CanonicalizeEmailAddressMatch') and
- my $replace = RT->Config->Get('CanonicalizeEmailAddressReplace') )
- {
- $email =~ s/$match/$replace/gi;
+ if ($RT::CanonicalizeEmailAddressMatch && $RT::CanonicalizeEmailAddressReplace ) {
+ $email =~ s/$RT::CanonicalizeEmailAddressMatch/$RT::CanonicalizeEmailAddressReplace/gi;
}
return ($email);
}
+
+# }}}
+
+# {{{ sub CanonicalizeUserInfo
+
+
+
=head2 CanonicalizeUserInfo HASH of ARGS
CanonicalizeUserInfo can convert all User->Create options.
@@ -656,9 +715,14 @@ sub CanonicalizeUserInfo {
}
-=head2 Password and authentication related functions
+# }}}
+
+
+# {{{ Password related functions
-=head3 SetRandomPassword
+# {{{ sub SetRandomPassword
+
+=head2 SetRandomPassword
Takes no arguments. Returns a status code and a new password or an error message.
If the status is 1, the second value returned is the new password.
@@ -674,8 +738,8 @@ sub SetRandomPassword {
}
- my $min = ( RT->Config->Get('MinimumPasswordLength') > 6 ? RT->Config->Get('MinimumPasswordLength') : 6);
- my $max = ( RT->Config->Get('MinimumPasswordLength') > 8 ? RT->Config->Get('MinimumPasswordLength') : 8);
+ my $min = ( $RT::MinimumPasswordLength > 6 ? $RT::MinimumPasswordLength : 6);
+ my $max = ( $RT::MinimumPasswordLength > 8 ? $RT::MinimumPasswordLength : 8);
my $pass = $self->GenerateRandomPassword( $min, $max) ;
@@ -691,7 +755,11 @@ sub SetRandomPassword {
}
-=head3 ResetPassword
+# }}}
+
+# {{{ sub ResetPassword
+
+=head2 ResetPassword
Returns status, [ERROR or new password]. Resets this user\'s password to
a randomly generated pronouncable password and emails them, using a
@@ -713,13 +781,38 @@ sub ResetPassword {
return ( 0, "$pass" );
}
- my $ret = RT::Interface::Email::SendEmailUsingTemplate(
- To => $self->EmailAddress,
- Template => 'PasswordChange',
- Arguments => {
- NewPassword => $pass,
- },
- );
+ my $template = RT::Template->new( $self->CurrentUser );
+
+ if ( $self->Privileged ) {
+ $template->LoadGlobalTemplate('RT_PasswordChange_Privileged');
+ }
+ else {
+ $template->LoadGlobalTemplate('RT_PasswordChange_NonPrivileged');
+ }
+
+ unless ( $template->Id ) {
+ $template->LoadGlobalTemplate('RT_PasswordChange');
+ }
+
+ unless ( $template->Id ) {
+ $RT::Logger->crit( "$self tried to send "
+ . $self->Name
+ . " a password reminder "
+ . "but couldn't find a password change template" );
+ }
+
+ my $notification = RT::Action::SendPasswordEmail->new(
+ TemplateObj => $template,
+ Argument => $pass
+ );
+
+ $notification->SetHeader( 'To', $self->EmailAddress );
+
+ my ($ret);
+ $ret = $notification->Prepare();
+ if ($ret) {
+ $ret = $notification->Commit();
+ }
if ($ret) {
return ( 1, $self->loc('New password notification sent') );
@@ -730,7 +823,11 @@ sub ResetPassword {
}
-=head3 GenerateRandomPassword MIN_LEN and MAX_LEN
+# }}}
+
+# {{{ sub GenerateRandomPassword
+
+=head2 GenerateRandomPassword MIN_LEN and MAX_LEN
Returns a random password between MIN_LEN and MAX_LEN characters long.
@@ -916,7 +1013,11 @@ sub _GenerateRandomNextChar {
return ($i);
}
-=head3 SetPassword
+# }}}
+
+# {{{ sub SetPassword
+
+=head2 SetPassword
Takes a string. Checks the string's length and sets this user's password
to that string.
@@ -934,8 +1035,8 @@ sub SetPassword {
if ( !$password ) {
return ( 0, $self->loc("No password set") );
}
- elsif ( length($password) < RT->Config->Get('MinimumPasswordLength') ) {
- return ( 0, $self->loc("Password needs to be at least [_1] characters long", RT->Config->Get('MinimumPasswordLength')) );
+ elsif ( length($password) < $RT::MinimumPasswordLength ) {
+ return ( 0, $self->loc("Password needs to be at least [_1] characters long", $RT::MinimumPasswordLength) );
}
else {
my $new = !$self->HasPassword;
@@ -952,7 +1053,7 @@ sub SetPassword {
}
-=head3 _GeneratePassword PASSWORD
+=head2 _GeneratePassword PASSWORD
returns an MD5 hash of the password passed in, in hexadecimal encoding.
@@ -968,7 +1069,7 @@ sub _GeneratePassword {
}
-=head3 _GeneratePasswordBase64 PASSWORD
+=head2 _GeneratePasswordBase64 PASSWORD
returns an MD5 hash of the password passed in, in base64 encoding
(obsoleted now).
@@ -985,12 +1086,16 @@ sub _GeneratePasswordBase64 {
}
-=head3 HasPassword
+# }}}
+
+
+=head2 HasPassword
Returns true if the user has a valid password, otherwise returns false.
=cut
+
sub HasPassword {
my $self = shift;
my $pwd = $self->__Value('Password');
@@ -1000,7 +1105,10 @@ sub HasPassword {
return 1;
}
-=head3 IsPassword
+
+# {{{ sub IsPassword
+
+=head2 IsPassword
Returns true if the passed in value is this user's password.
Returns undef otherwise.
@@ -1047,80 +1155,13 @@ sub IsPassword {
return (undef);
}
-=head3 AuthToken
+# }}}
-Returns an authentication string associated with the user. This
-string can be used to generate passwordless URLs to integrate
-RT with services and programms like callendar managers, rss
-readers and other.
-
-=cut
-
-sub AuthToken {
- my $self = shift;
- my $secret = $self->FirstAttribute("AuthToken");
- return $secret->Content if $secret;
-
- my $id = $self->id;
- $self = RT::User->new( $RT::SystemUser );
- $self->Load( $id );
- $secret = substr(Digest::MD5::md5_hex(time . {} . rand()),0,16);
- my ($status, $msg) = $self->SetAttribute( Name => "AuthToken", Content => $secret );
- unless ( $status ) {
- $RT::Logger->error( "Couldn't set auth token: $msg" );
- return undef;
- }
- return $secret;
-}
-
-=head3 GenerateAuthToken
-
-Generate a random authentication string for the user.
-
-=cut
-
-sub GenerateAuthToken {
- my $self = shift;
- my $token = substr(Digest::MD5::md5_hex(time . {} . rand()),0,16);
- return $self->SetAttribute( Name => "AuthToken", Content => $token );
-}
-
-=head3 GenerateAuthString
-
-Takes a string and returns back a hex hash string. Later you can use
-this pair to make sure it's generated by this user using L</ValidateAuthString>
-
-=cut
-
-sub GenerateAuthString {
- my $self = shift;
- my $protect = shift;
-
- my $str = $self->AuthToken . $protect;
- utf8::encode($str);
-
- return substr(Digest::MD5::md5_hex($str),0,16);
-}
+# }}}
-=head3 ValidateAuthString
+# {{{ sub SetDisabled
-Takes auth string and protected string. Returns true is protected string
-has been protected by user's L</AuthToken>. See also L</GenerateAuthString>.
-
-=cut
-
-sub ValidateAuthString {
- my $self = shift;
- my $auth_string = shift;
- my $protected = shift;
-
- my $str = $self->AuthToken . $protected;
- utf8::encode( $str );
-
- return $auth_string eq substr(Digest::MD5::md5_hex($str),0,16);
-}
-
-=head2 SetDisabled
+=head2 Sub SetDisabled
Toggles the user's disabled flag.
If this flag is
@@ -1129,69 +1170,53 @@ user will fail. The user will appear in no user listings.
=cut
+# }}}
+
sub SetDisabled {
my $self = shift;
- my $val = shift;
unless ( $self->CurrentUser->HasRight(Right => 'AdminUsers', Object => $RT::System) ) {
return (0, $self->loc('Permission Denied'));
}
-
- $RT::Handle->BeginTransaction();
- my $set_err = $self->PrincipalObj->SetDisabled($val);
- unless ($set_err) {
- $RT::Handle->Rollback();
- $RT::Logger->warning(sprintf("Couldn't %s user %s", ($val == 1) ? "disable" : "enable", $self->PrincipalObj->Id));
- return (undef);
- }
- $self->_NewTransaction( Type => ($val == 1) ? "Disabled" : "Enabled" );
-
- $RT::Handle->Commit();
-
- if ( $val == 1 ) {
- return (1, $self->loc("User disabled"));
- } else {
- return (1, $self->loc("User enabled"));
- }
-
+ return $self->PrincipalObj->SetDisabled(@_);
}
-=head2 Disabled
-
-Returns true if user is disabled or false otherwise
-
-=cut
-
sub Disabled {
my $self = shift;
return $self->PrincipalObj->Disabled(@_);
}
+
+# {{{ Principal related routines
+
=head2 PrincipalObj
Returns the principal object for this user. returns an empty RT::Principal
if there's no principal object matching this user.
The response is cached. PrincipalObj should never ever change.
+=begin testing
+
+ok(my $u = RT::User->new($RT::SystemUser));
+ok($u->Load(1), "Loaded the first user");
+ok($u->PrincipalObj->ObjectId == 1, "user 1 is the first principal");
+is($u->PrincipalObj->PrincipalType, 'User' , "Principal 1 is a user, not a group");
+
+=end testing
+
=cut
+
sub PrincipalObj {
my $self = shift;
+ unless ($self->{'PrincipalObj'} &&
+ ($self->{'PrincipalObj'}->ObjectId == $self->Id) &&
+ ($self->{'PrincipalObj'}->PrincipalType eq 'User')) {
- unless ( $self->id ) {
- $RT::Logger->error("Couldn't get principal for not loaded object");
- return undef;
- }
-
- my $obj = RT::Principal->new( $self->CurrentUser );
- $obj->LoadById( $self->id );
- unless ( $obj->id ) {
- $RT::Logger->crit( 'No principal for user #'. $self->id );
- return undef;
- } elsif ( $obj->PrincipalType ne 'User' ) {
- $RT::Logger->crit( 'User #'. $self->id .' has principal of '. $obj->PrincipalType .' type' );
- return undef;
- }
- return $obj;
+ $self->{'PrincipalObj'} = RT::Principal->new($self->CurrentUser);
+ $self->{'PrincipalObj'}->LoadByCols('ObjectId' => $self->Id,
+ 'PrincipalType' => 'User') ;
+ }
+ return($self->{'PrincipalObj'});
}
@@ -1206,6 +1231,12 @@ sub PrincipalId {
return $self->Id;
}
+# }}}
+
+
+
+# {{{ sub HasGroupRight
+
=head2 HasGroupRight
Takes a paramhash which can contain
@@ -1236,11 +1267,14 @@ sub HasGroupRight {
$args{'GroupObj'}->Load( $args{'Group'} );
}
- # Validate and load up the GroupId
+ # {{{ Validate and load up the GroupId
unless ( ( defined $args{'GroupObj'} ) and ( $args{'GroupObj'}->Id ) ) {
return undef;
}
+ # }}}
+
+
# Figure out whether a user has the right we're asking about.
my $retval = $self->HasRight(
Object => $args{'GroupObj'},
@@ -1248,8 +1282,14 @@ sub HasGroupRight {
);
return ($retval);
+
+
}
+# }}}
+
+# {{{ sub OwnGroups
+
=head2 OwnGroups
Returns a group collection object containing the groups of which this
@@ -1262,21 +1302,533 @@ sub OwnGroups {
my $groups = RT::Groups->new($self->CurrentUser);
$groups->LimitToUserDefinedGroups;
$groups->WithMember(PrincipalId => $self->Id,
- Recursively => 1);
+ Recursively => 1);
return $groups;
}
+# }}}
+
+# {{{ Links
+
+#much false laziness w/Ticket_Overlay.pm
+
+# A helper table for links mapping to make it easier
+# to build and parse links between tickets
+
+use vars '%LINKDIRMAP';
+
+%LINKDIRMAP = (
+ MemberOf => { Base => 'MemberOf',
+ Target => 'HasMember', },
+ RefersTo => { Base => 'RefersTo',
+ Target => 'ReferredToBy', },
+ DependsOn => { Base => 'DependsOn',
+ Target => 'DependedOnBy', },
+ MergedInto => { Base => 'MergedInto',
+ Target => 'MergedInto', },
+
+);
+
+sub LINKDIRMAP { return \%LINKDIRMAP }
+
+#sub _Links {
+# my $self = shift;
+#
+# #TODO: Field isn't the right thing here. but I ahave no idea what mnemonic ---
+# #tobias meant by $f
+# my $field = shift;
+# my $type = shift || "";
+#
+# unless ( $self->{"$field$type"} ) {
+# $self->{"$field$type"} = new RT::Links( $self->CurrentUser );
+# if ( $self->CurrentUserHasRight('ShowTicket') ) {
+# # Maybe this ticket is a merged ticket
+# my $Tickets = new RT::Tickets( $self->CurrentUser );
+# # at least to myself
+# $self->{"$field$type"}->Limit( FIELD => $field,
+# VALUE => $self->URI,
+# ENTRYAGGREGATOR => 'OR' );
+# $Tickets->Limit( FIELD => 'EffectiveId',
+# VALUE => $self->EffectiveId );
+# while (my $Ticket = $Tickets->Next) {
+# $self->{"$field$type"}->Limit( FIELD => $field,
+# VALUE => $Ticket->URI,
+# ENTRYAGGREGATOR => 'OR' );
+# }
+# $self->{"$field$type"}->Limit( FIELD => 'Type',
+# VALUE => $type )
+# if ($type);
+# }
+# }
+# return ( $self->{"$field$type"} );
+#}
+
+=head2 DeleteLink
+
+Delete a link. takes a paramhash of Base, Target and Type.
+Either Base or Target must be null. The null value will
+be replaced with this ticket\'s id
+
+=cut
+
+sub DeleteLink {
+ my $self = shift;
+ my %args = (
+ Base => undef,
+ Target => undef,
+ Type => undef,
+ @_
+ );
+
+ unless ( $args{'Target'} || $args{'Base'} ) {
+ $RT::Logger->error("Base or Target must be specified\n");
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ #check acls
+ my $right = 0;
+ $right++ if $self->CurrentUserHasRight('ModifyUser');
+ if ( !$right && $RT::StrictLinkACL ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
+# return (0, $msg) unless $status;
+# if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
+# $right++;
+# }
+# if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+# ( $RT::StrictLinkACL && $right < 2 ) )
+# {
+# return ( 0, $self->loc("Permission Denied") );
+# }
+
+ my ($val, $Msg) = $self->SUPER::_DeleteLink(%args);
+
+ if ( !$val ) {
+ $RT::Logger->debug("Couldn't find that link\n");
+ return ( 0, $Msg );
+ }
+
+ my ($direction, $remote_link);
+
+ if ( $args{'Base'} ) {
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+ elsif ( $args{'Target'} ) {
+ $remote_link = $args{'Target'};
+ $direction='Base';
+ }
+
+ if ( $args{'Silent'} ) {
+ return ( $val, $Msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ my ( $Trans, $Msg, $TransObj ) = $self->_NewTransaction(
+ Type => 'DeleteLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ OldValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0
+ );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'DeleteLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ OldValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+
+ return ( $Trans, $Msg );
+ }
+}
+
+sub AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ unless ( $args{'Target'} || $args{'Base'} ) {
+ $RT::Logger->error("Base or Target must be specified\n");
+ return ( 0, $self->loc('Either base or target must be specified') );
+ }
+
+ my $right = 0;
+ $right++ if $self->CurrentUserHasRight('ModifyUser');
+ if ( !$right && $RT::StrictLinkACL ) {
+ return ( 0, $self->loc("Permission Denied") );
+ }
+
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my ($status, $msg, $other_ticket) = $self->__GetTicketFromURI( URI => $args{'Target'} || $args{'Base'} );
+# return (0, $msg) unless $status;
+# if ( !$other_ticket || $other_ticket->CurrentUserHasRight('ModifyTicket') ) {
+# $right++;
+# }
+# if ( ( !$RT::StrictLinkACL && $right == 0 ) ||
+# ( $RT::StrictLinkACL && $right < 2 ) )
+# {
+# return ( 0, $self->loc("Permission Denied") );
+# }
+
+ return $self->_AddLink(%args);
+}
+
+#sub __GetTicketFromURI {
+# my $self = shift;
+# my %args = ( URI => '', @_ );
+#
+# # If the other URI is an RT::Ticket, we want to make sure the user
+# # can modify it too...
+# my $uri_obj = RT::URI->new( $self->CurrentUser );
+# $uri_obj->FromURI( $args{'URI'} );
+#
+# unless ( $uri_obj->Resolver && $uri_obj->Scheme ) {
+# my $msg = $self->loc( "Couldn't resolve '[_1]' into a URI.", $args{'URI'} );
+# $RT::Logger->warning( "$msg\n" );
+# return( 0, $msg );
+# }
+# my $obj = $uri_obj->Resolver->Object;
+# unless ( UNIVERSAL::isa($obj, 'RT::Ticket') && $obj->id ) {
+# return (1, 'Found not a ticket', undef);
+# }
+# return (1, 'Found ticket', $obj);
+#}
+
+=head2 _AddLink
+
+Private non-acled variant of AddLink so that links can be added during create.
+
+=cut
+
+sub _AddLink {
+ my $self = shift;
+ my %args = ( Target => '',
+ Base => '',
+ Type => '',
+ Silent => undef,
+ @_ );
+
+ my ($val, $msg, $exist) = $self->SUPER::_AddLink(%args);
+ return ($val, $msg) if !$val || $exist;
+
+ my ($direction, $remote_link);
+ if ( $args{'Target'} ) {
+ $remote_link = $args{'Target'};
+ $direction = 'Base';
+ } elsif ( $args{'Base'} ) {
+ $remote_link = $args{'Base'};
+ $direction = 'Target';
+ }
+
+ # Don't write the transaction if we're doing this on create
+ if ( $args{'Silent'} ) {
+ return ( $val, $msg );
+ }
+ else {
+ my $remote_uri = RT::URI->new( $self->CurrentUser );
+ $remote_uri->FromURI( $remote_link );
+
+ #Write the transaction
+ my ( $Trans, $Msg, $TransObj ) =
+ $self->_NewTransaction(Type => 'AddLink',
+ Field => $LINKDIRMAP{$args{'Type'}}->{$direction},
+ NewValue => $remote_uri->URI || $remote_link,
+ TimeTaken => 0 );
+
+ if ( $remote_uri->IsLocal ) {
+
+ my $OtherObj = $remote_uri->Object;
+ my ( $val, $Msg ) = $OtherObj->_NewTransaction(Type => 'AddLink',
+ Field => $direction eq 'Target' ? $LINKDIRMAP{$args{'Type'}}->{Base}
+ : $LINKDIRMAP{$args{'Type'}}->{Target},
+ NewValue => $self->URI,
+ ActivateScrips => ! $RT::LinkTransactionsRun1Scrip,
+ TimeTaken => 0 );
+ }
+ return ( $val, $Msg );
+ }
+
+}
+
+
+
+# }}}
+
+
+# {{{ sub Rights testing
+
+=head1 Rights testing
+
+
+=begin testing
+
+my $root = RT::User->new($RT::SystemUser);
+$root->Load('root');
+ok($root->Id, "Found the root user");
+my $rootq = RT::Queue->new($root);
+$rootq->Load(1);
+ok($rootq->Id, "Loaded the first queue");
+
+ok ($rootq->CurrentUser->HasRight(Right=> 'CreateTicket', Object => $rootq), "Root can create tickets");
+
+my $new_user = RT::User->new($RT::SystemUser);
+my ($id, $msg) = $new_user->Create(Name => 'ACLTest'.$$);
+
+ok ($id, "Created a new user for acl test $msg");
+
+my $q = RT::Queue->new($new_user);
+$q->Load(1);
+ok($q->Id, "Loaded the first queue");
+
+
+ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "Some random user doesn't have the right to create tickets");
+ok (my ($gval, $gmsg) = $new_user->PrincipalObj->GrantRight( Right => 'CreateTicket', Object => $q), "Granted the random user the right to create tickets");
+ok ($gval, "Grant succeeded - $gmsg");
+
+
+ok ($q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can create tickets after we grant him the right");
+ok (my ($gval, $gmsg) = $new_user->PrincipalObj->RevokeRight( Right => 'CreateTicket', Object => $q), "revoked the random user the right to create tickets");
+ok ($gval, "Revocation succeeded - $gmsg");
+ok (!$q->CurrentUser->HasRight(Right => 'CreateTicket', Object => $q), "The user can't create tickets anymore");
+
+
+
+
+
+# Create a ticket in the queue
+my $new_tick = RT::Ticket->new($RT::SystemUser);
+my ($tickid, $tickmsg) = $new_tick->Create(Subject=> 'ACL Test', Queue => 'General');
+ok($tickid, "Created ticket: $tickid");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+# Create a new group
+my $group = RT::Group->new($RT::SystemUser);
+$group->CreateUserDefinedGroup(Name => 'ACLTest'.$$);
+ok($group->Id, "Created a new group Ok");
+# Grant a group the right to modify tickets in a queue
+ok(my ($gv,$gm) = $group->PrincipalObj->GrantRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
+ok($gv,"Grant succeeed - $gm");
+# Add the user to the group
+ok( my ($aid, $amsg) = $group->AddMember($new_user->PrincipalId), "Added the member to the group");
+ok ($aid, "Member added to group: $amsg");
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can modify the ticket with group membership");
+
+
+# Remove the user from the group
+ok( my ($did, $dmsg) = $group->DeleteMember($new_user->PrincipalId), "Deleted the member from the group");
+ok ($did,"Deleted the group member: $dmsg");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+
+my $q_as_system = RT::Queue->new($RT::SystemUser);
+$q_as_system->Load(1);
+ok($q_as_system->Id, "Loaded the first queue");
+
+# Create a ticket in the queue
+my $new_tick2 = RT::Ticket->new($RT::SystemUser);
+my ($tick2id, $tickmsg) = $new_tick2->Create(Subject=> 'ACL Test 2', Queue =>$q_as_system->Id);
+ok($tick2id, "Created ticket: $tick2id");
+is($new_tick2->QueueObj->id, $q_as_system->Id, "Created a new ticket in queue 1");
+
+
+# make sure that the user can't do this without subgroup membership
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+# Create a subgroup
+my $subgroup = RT::Group->new($RT::SystemUser);
+$subgroup->CreateUserDefinedGroup(Name => 'Subgrouptest',$$);
+ok($subgroup->Id, "Created a new group ".$subgroup->Id."Ok");
+#Add the subgroup as a subgroup of the group
+my ($said, $samsg) = $group->AddMember($subgroup->PrincipalId);
+ok ($said, "Added the subgroup as a member of the group");
+# Add the user to a subgroup of the group
+
+my ($usaid, $usamsg) = $subgroup->AddMember($new_user->PrincipalId);
+ok($usaid,"Added the user ".$new_user->Id."to the subgroup");
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket with subgroup membership");
+
+# {{{ Deal with making sure that members of subgroups of a disabled group don't have rights
+
+my ($id, $msg);
+($id, $msg) = $group->SetDisabled(1);
+ok ($id,$msg);
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$group->Id. " is disabled");
+ ($id, $msg) = $group->SetDisabled(0);
+ok($id,$msg);
+# Test what happens when we disable the group the user is a member of directly
+
+($id, $msg) = $subgroup->SetDisabled(1);
+ ok ($id,$msg);
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket when the group ".$subgroup->Id. " is disabled");
+ ($id, $msg) = $subgroup->SetDisabled(0);
+ ok ($id,$msg);
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket without group membership");
+
+# }}}
+
+
+my ($usrid, $usrmsg) = $subgroup->DeleteMember($new_user->PrincipalId);
+ok($usrid,"removed the user from the group - $usrmsg");
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+#revoke the right to modify tickets in a queue
+ok(($gv,$gm) = $group->PrincipalObj->RevokeRight( Object => $q, Right => 'ModifyTicket'),"Granted the group the right to modify tickets");
+ok($gv,"revoke succeeed - $gm");
+
+# {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _queue_ level
+
+# Grant queue admin cc the right to modify ticket in the queue
+ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $q_as_system, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
+ok($qv, "Granted the right successfully - $qm");
+
+# Add the user as a queue admincc
+ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+# }}}
+
+# {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
+
+# Add the user as a ticket admincc
+ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+
+
+# Revoke the right to modify ticket in the queue
+ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $q_as_system, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
+ok($rqv, "Revoked the right successfully - $rqm");
+
+# }}}
+
+
+
+# {{{ Test the user's right to modify a ticket as a _queue_ admincc for a right granted at the _system_ level
+
+# Before we start Make sure the user does not have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without it being granted");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without it being granted");
+
+# Grant queue admin cc the right to modify ticket in the queue
+ok(my ($qv,$qm) = $q_as_system->AdminCc->PrincipalObj->GrantRight( Object => $RT::System, Right => 'ModifyTicket'),"Granted the queue adminccs the right to modify tickets");
+ok($qv, "Granted the right successfully - $qm");
+
+# Make sure the user can't modify the ticket before they're added as a watcher
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue without being an admincc");
+
+# Add the user as a queue admincc
+ok ((my $add_id, $add_msg) = $q_as_system->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+ok ($new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can modify tickets in the queue as an admincc");
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $q_as_system->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without group membership");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can't modify tickets in the queue without group membership");
+
+# }}}
+
+# {{{ Test the user's right to modify a ticket as a _ticket_ admincc with the right granted at the _queue_ level
+
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can not modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
+
+
+# Add the user as a ticket admincc
+ok ((my $uadd_id, $uadd_msg) = $new_tick2->AddWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Added the new user as a queue admincc");
+ok ($add_id, "the user is now a queue admincc - $add_msg");
+
+# Make sure the user does have the right to modify tickets in the queue
+ok ($new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can modify the ticket as an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj being only a ticket admincc");
+
+# Remove the user from the role group
+ok ((my $del_id, $del_msg) = $new_tick2->DeleteWatcher(Type => 'AdminCc', PrincipalId => $new_user->PrincipalId) , "Deleted the new user as a queue admincc");
+
+# Make sure the user doesn't have the right to modify tickets in the queue
+ok (!$new_user->HasRight( Object => $new_tick2, Right => 'ModifyTicket'), "User can't modify the ticket without being an admincc");
+ok (!$new_user->HasRight( Object => $new_tick2->QueueObj, Right => 'ModifyTicket'), "User can not modify tickets in the queue obj without being an admincc");
+
+
+# Revoke the right to modify ticket in the queue
+ok(my ($rqv,$rqm) = $q_as_system->AdminCc->PrincipalObj->RevokeRight( Object => $RT::System, Right => 'ModifyTicket'),"Revokeed the queue adminccs the right to modify tickets");
+ok($rqv, "Revoked the right successfully - $rqm");
+
+# }}}
+
+
+
+
+# Grant "privileged users" the system right to create users
+# Create a privileged user.
+# have that user create another user
+# Revoke the right for privileged users to create users
+# have the privileged user try to create another user and fail the ACL check
+
+=end testing
+
+=cut
+
+# }}}
+
+
+# {{{ sub HasRight
+
=head2 HasRight
-Shim around PrincipalObj->HasRight. See L<RT::Principal>.
+Shim around PrincipalObj->HasRight. See RT::Principal
=cut
sub HasRight {
+
my $self = shift;
return $self->PrincipalObj->HasRight(@_);
}
+# }}}
+
+# {{{ sub CurrentUserCanModify
+
=head2 CurrentUserCanModify RIGHT
If the user has rights for this object, either because
@@ -1314,6 +1866,10 @@ sub CurrentUserCanModify {
}
+# }}}
+
+# {{{ sub CurrentUserHasRight
+
=head2 CurrentUserHasRight
Takes a single argument. returns 1 if $Self->CurrentUser
@@ -1331,17 +1887,19 @@ sub CurrentUserHasRight {
sub _PrefName {
my $name = shift;
if (ref $name) {
- $name = ref($name).'-'.$name->Id;
+ $name = ref ($name).'-'.$name->Id;
}
return 'Pref-'.$name;
}
+# {{{ sub Preferences
+
=head2 Preferences NAME/OBJ DEFAULT
-Obtain user preferences associated with given object or name.
-Returns DEFAULT if no preferences found. If DEFAULT is a hashref,
-override the entries with user preferences.
+ Obtain user preferences associated with given object or name.
+ Returns DEFAULT if no preferences found. If DEFAULT is a hashref,
+ override the entries with user preferences.
=cut
@@ -1350,49 +1908,53 @@ sub Preferences {
my $name = _PrefName (shift);
my $default = shift;
- my $attr = RT::Attribute->new( $self->CurrentUser );
- $attr->LoadByNameAndObject( Object => $self, Name => $name );
+ my $attr = RT::Attribute->new ($self->CurrentUser);
+ $attr->LoadByNameAndObject (Object => $self, Name => $name);
my $content = $attr->Id ? $attr->Content : undef;
- unless ( ref $content eq 'HASH' ) {
- return defined $content ? $content : $default;
+ if (ref ($content) eq 'HASH') {
+ if (ref ($default) eq 'HASH') {
+ for (keys %$default) {
+ exists $content->{$_} or $content->{$_} = $default->{$_};
+ }
+ }
+ elsif (defined $default) {
+ $RT::Logger->error("Preferences $name for user".$self->Id." is hash but default is not");
+ }
+ return $content;
}
-
- if (ref $default eq 'HASH') {
- for (keys %$default) {
- exists $content->{$_} or $content->{$_} = $default->{$_};
- }
- }
- elsif (defined $default) {
- $RT::Logger->error("Preferences $name for user".$self->Id." is hash but default is not");
+ else {
+ return defined $content ? $content : $default;
}
- return $content;
}
+# }}}
+
+# {{{ sub SetPreferences
+
=head2 SetPreferences NAME/OBJ VALUE
-Set user preferences associated with given object or name.
+ Set user preferences associated with given object or name.
=cut
sub SetPreferences {
- my $self = shift;
- my $name = _PrefName( shift );
+ my $self = shift;
+ my $name = _PrefName (shift);
my $value = shift;
-
- return (0, $self->loc("No permission to set preferences"))
- unless $self->CurrentUserCanModify('Preferences');
-
- my $attr = RT::Attribute->new( $self->CurrentUser );
- $attr->LoadByNameAndObject( Object => $self, Name => $name );
- if ( $attr->Id ) {
- return $attr->SetContent( $value );
+ my $attr = RT::Attribute->new ($self->CurrentUser);
+ $attr->LoadByNameAndObject (Object => $self, Name => $name);
+ if ($attr->Id) {
+ return $attr->SetContent ($value);
}
else {
- return $self->AddAttribute( Name => $name, Content => $value );
+ return $self->AddAttribute ( Name => $name, Content => $value );
}
}
+# }}}
+
+
=head2 WatchedQueues ROLE_LIST
Returns a RT::Queues object containing every queue watched by the user.
@@ -1462,6 +2024,9 @@ sub WatchedQueues {
}
+
+# {{{ sub _CleanupInvalidDelegations
+
=head2 _CleanupInvalidDelegations { InsideTransaction => undef }
Revokes all ACE entries delegated by this user which are inconsistent
@@ -1484,59 +2049,63 @@ and logs an internal error if the deletion fails (should not happen).
sub _CleanupInvalidDelegations {
my $self = shift;
my %args = ( InsideTransaction => undef,
- @_ );
+ @_ );
unless ( $self->Id ) {
- $RT::Logger->warning("User not loaded.");
- return (undef);
+ $RT::Logger->warning("User not loaded.");
+ return (undef);
}
my $in_trans = $args{InsideTransaction};
return(1) if ($self->HasRight(Right => 'DelegateRights',
- Object => $RT::System));
+ Object => $RT::System));
# Look up all delegation rights currently posessed by this user.
my $deleg_acl = RT::ACL->new($RT::SystemUser);
$deleg_acl->LimitToPrincipal(Type => 'User',
- Id => $self->PrincipalId,
- IncludeGroupMembership => 1);
+ Id => $self->PrincipalId,
+ IncludeGroupMembership => 1);
$deleg_acl->Limit( FIELD => 'RightName',
- OPERATOR => '=',
- VALUE => 'DelegateRights' );
+ OPERATOR => '=',
+ VALUE => 'DelegateRights' );
my @allowed_deleg_objects = map {$_->Object()}
- @{$deleg_acl->ItemsArrayRef()};
+ @{$deleg_acl->ItemsArrayRef()};
# Look up all rights delegated by this principal which are
# inconsistent with the allowed delegation objects.
my $acl_to_del = RT::ACL->new($RT::SystemUser);
$acl_to_del->DelegatedBy(Id => $self->Id);
foreach (@allowed_deleg_objects) {
- $acl_to_del->LimitNotObject($_);
+ $acl_to_del->LimitNotObject($_);
}
# Delete all disallowed delegations
while ( my $ace = $acl_to_del->Next() ) {
- my $ret = $ace->_Delete(InsideTransaction => 1);
- unless ($ret) {
- $RT::Handle->Rollback() unless $in_trans;
- $RT::Logger->warning("Couldn't delete delegated ACL entry ".$ace->Id);
- return (undef);
- }
+ my $ret = $ace->_Delete(InsideTransaction => 1);
+ unless ($ret) {
+ $RT::Handle->Rollback() unless $in_trans;
+ $RT::Logger->warning("Couldn't delete delegated ACL entry ".$ace->Id);
+ return (undef);
+ }
}
$RT::Handle->Commit() unless $in_trans;
return (1);
}
+# }}}
+
+# {{{ sub _Set
+
sub _Set {
my $self = shift;
my %args = (
Field => undef,
Value => undef,
- TransactionType => 'Set',
- RecordTransaction => 1,
+ TransactionType => 'Set',
+ RecordTransaction => 1,
@_
);
@@ -1553,7 +2122,7 @@ sub _Set {
my $Old = $self->SUPER::_Value("$args{'Field'}");
my ($ret, $msg) = $self->SUPER::_Set( Field => $args{'Field'},
- Value => $args{'Value'} );
+ Value => $args{'Value'} );
#If we can't actually set the field to the value, don't record
# a transaction. instead, get out of here.
@@ -1575,6 +2144,10 @@ sub _Set {
}
}
+# }}}
+
+# {{{ sub _Value
+
=head2 _Value
Takes the name of a table column.
@@ -1602,7 +2175,7 @@ sub _Value {
#If the user wants to see their own values, let them
# TODO figure ouyt a better way to deal with this
- elsif ( defined($self->Id) && $self->CurrentUser->Id == $self->Id ) {
+ elsif ( $self->CurrentUser->Id == $self->Id ) {
return ( $self->SUPER::_Value($field) );
}
@@ -1616,96 +2189,14 @@ sub _Value {
}
-=head2 FriendlyName
-
-Return the friendly name
-
-=cut
-
-sub FriendlyName {
- my $self = shift;
- return $self->RealName if defined($self->RealName);
- return $self->Name if defined($self->Name);
- return "";
-}
-
-=head2 PreferredKey
-
-Returns the preferred key of the user. If none is set, then this will query
-GPG and set the preferred key to the maximally trusted key found (and then
-return it). Returns C<undef> if no preferred key can be found.
-
-=cut
-
-sub PreferredKey
-{
- my $self = shift;
- return undef unless RT->Config->Get('GnuPG')->{'Enable'};
- my $prefkey = $self->FirstAttribute('PreferredKey');
- return $prefkey->Content if $prefkey;
-
- # we don't have a preferred key for this user, so now we must query GPG
- require RT::Crypt::GnuPG;
- my %res = RT::Crypt::GnuPG::GetKeysForEncryption($self->EmailAddress);
- return undef unless defined $res{'info'};
- my @keys = @{ $res{'info'} };
- return undef if @keys == 0;
-
- if (@keys == 1) {
- $prefkey = $keys[0]->{'Fingerprint'};
- }
- else {
- # prefer the maximally trusted key
- @keys = sort { $b->{'TrustLevel'} <=> $a->{'TrustLevel'} } @keys;
- $prefkey = $keys[0]->{'Fingerprint'};
- }
-
- $self->SetAttribute(Name => 'PreferredKey', Content => $prefkey);
- return $prefkey;
-}
-
-sub PrivateKey {
- my $self = shift;
-
- my $key = $self->FirstAttribute('PrivateKey') or return undef;
- return $key->Content;
-}
-
-sub SetPrivateKey {
- my $self = shift;
- my $key = shift;
- # XXX: ACL
- unless ( $key ) {
- my ($status, $msg) = $self->DeleteAttribute('PrivateKey');
- unless ( $status ) {
- $RT::Logger->error( "Couldn't delete attribute: $msg" );
- return ($status, $self->loc("Couldn't unset private key"));
- }
- return ($status, $self->loc("Unset private key"));
- }
-
- # check that it's really private key
- {
- my %tmp = RT::Crypt::GnuPG::GetKeysForSigning( $key );
- return (0, $self->loc("No such key or it's not suitable for signing"))
- if $tmp{'exit_code'} || !$tmp{'info'};
- }
-
- my ($status, $msg) = $self->SetAttribute(
- Name => 'PrivateKey',
- Content => $key,
- );
- return ($status, $self->loc("Couldn't set private key"))
- unless $status;
- return ($status, $self->loc("Unset private key"));
-}
+# }}}
sub BasicColumns {
(
- [ Name => 'User Id' ],
- [ EmailAddress => 'Email' ],
- [ RealName => 'Name' ],
- [ Organization => 'Organization' ],
+ [ Name => 'User Id' ],
+ [ EmailAddress => 'Email' ],
+ [ RealName => 'Name' ],
+ [ Organization => 'Organization' ],
);
}
diff --git a/rt/lib/RT/Users.pm b/rt/lib/RT/Users.pm
index 3a6eefff7..d58f69653 100755
--- a/rt/lib/RT/Users.pm
+++ b/rt/lib/RT/Users.pm
@@ -1,14 +1,8 @@
-# BEGIN BPS TAGGED BLOCK {{{
+# BEGIN LICENSE BLOCK
#
-# COPYRIGHT:
+# Copyright (c) 1996-2003 Jesse Vincent <jesse@bestpractical.com>
#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
+# (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
@@ -20,32 +14,13 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
+# 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.
#
-# CONTRIBUTION SUBMISSION POLICY:
#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-
+# END LICENSE BLOCK
# Autogenerated by DBIx::SearchBuilder factory (by <jesse@bestpractical.com>)
# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.
#
@@ -89,7 +64,7 @@ sub _Init {
}
-=head2 NewItem
+=item NewItem
Returns an empty new RT::User item
@@ -126,7 +101,7 @@ _Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customiz
These overlay files can contain new subs or subs to replace existing subs in this module.
-Each of these files should begin with the line
+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);
diff --git a/rt/lib/RT/Users_Overlay.pm b/rt/lib/RT/Users_Overlay.pm
index a2d235c23..bdc7074ff 100644
--- a/rt/lib/RT/Users_Overlay.pm
+++ b/rt/lib/RT/Users_Overlay.pm
@@ -1,8 +1,8 @@
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -45,7 +45,6 @@
# those contributions and any derivatives thereof.
#
# END BPS TAGGED BLOCK }}}
-
=head1 NAME
RT::Users - Collection of RT::User objects
@@ -60,6 +59,11 @@
=head1 METHODS
+=begin testing
+
+ok(require RT::Users);
+
+=end testing
=cut
@@ -228,6 +232,68 @@ sub LimitToPrivileged {
=head2 WhoHaveRight { Right => 'name', Object => $rt_object , IncludeSuperusers => undef, IncludeSubgroupMembers => undef, IncludeSystemRights => undef, EquivObjects => [ ] }
+=begin testing
+
+ok(my $users = RT::Users->new($RT::SystemUser));
+$users->WhoHaveRight(Object =>$RT::System, Right =>'SuperUser');
+ok($users->Count == 1, "There is one privileged superuser - Found ". $users->Count );
+# TODO: this wants more testing
+
+my $RTxUser = RT::User->new($RT::SystemUser);
+($id, $msg) = $RTxUser->Create( Name => 'RTxUser', Comments => "RTx extension user", Privileged => 1);
+ok ($id,$msg);
+
+my $group = RT::Group->new($RT::SystemUser);
+$group->LoadACLEquivalenceGroup($RTxUser->PrincipalObj);
+
+my $RTxSysObj = {};
+bless $RTxSysObj, 'RTx::System';
+*RTx::System::Id = sub { 1; };
+*RTx::System::id = *RTx::System::Id;
+my $ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System', ObjectId => 1 );
+ok ($id, "ACL for RTxSysObj created");
+
+my $RTxObj = {};
+bless $RTxObj, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 4; };
+*RTx::System::Record::id = *RTx::System::Record::Id;
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxSysObj);
+is($users->Count, 1, "RTxUserRight found for RTxSysObj");
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj);
+is($users->Count, 0, "RTxUserRight not found for RTxObj");
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj, EquivObjects => [ $RTxSysObj ]);
+is($users->Count, 1, "RTxUserRight found for RTxObj using EquivObjects");
+
+$ace = RT::Record->new($RT::SystemUser);
+$ace->Table('ACL');
+$ace->_BuildTableAttributes unless ($_TABLE_ATTR->{ref($self)});
+($id, $msg) = $ace->Create( PrincipalId => $group->id, PrincipalType => 'Group', RightName => 'RTxUserRight', ObjectType => 'RTx::System::Record', ObjectId => 5 );
+ok ($id, "ACL for RTxObj created");
+
+my $RTxObj2 = {};
+bless $RTxObj2, 'RTx::System::Record';
+*RTx::System::Record::Id = sub { 5; };
+*RTx::System::Record::id = sub { 5; };
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2);
+is($users->Count, 1, "RTxUserRight found for RTxObj2");
+
+$users = RT::Users->new($RT::SystemUser);
+$users->WhoHaveRight(Right => 'RTxUserRight', Object => $RTxObj2, EquivObjects => [ $RTxSysObj ]);
+is($users->Count, 1, "RTxUserRight found for RTxObj2");
+
+
+=end testing
find all users who the right Right for this group, either individually
or as members of groups
@@ -297,16 +363,6 @@ sub _JoinACL
@_,
);
- if ( $args{'Right'} ) {
- my $canonic = RT::ACE->CanonicalizeRightName( $args{'Right'} );
- unless ( $canonic ) {
- $RT::Logger->error("Invalid right. Couldn't canonicalize right '$args{'Right'}'");
- }
- else {
- $args{'Right'} = $canonic;
- }
- }
-
my $acl = $self->NewAlias('ACL');
$self->Limit(
ALIAS => $acl,
@@ -347,7 +403,7 @@ sub _GetEquivObjects
# XXX: This should be abstracted into object itself
if( $args{'Object'}->id ) {
- push @objects, $args{'Object'}->ACLEquivalenceObjects;
+ push @objects, $args{'Object'}->QueueObj;
} else {
push @objects, 'RT::Queue';
}
@@ -384,6 +440,7 @@ sub WhoHaveRight {
$from_group->WhoHaveGroupRight( %args );
#XXX: DIRTY HACK
+ use DBIx::SearchBuilder 1.50; #no version on ::Union :(
use DBIx::SearchBuilder::Union;
my $union = new DBIx::SearchBuilder::Union;
$union->add( $_ ) foreach @from_role;
diff --git a/rt/lib/RTx/Statistics.pm b/rt/lib/RTx/Statistics.pm
new file mode 100755
index 000000000..8b9d6e4f0
--- /dev/null
+++ b/rt/lib/RTx/Statistics.pm
@@ -0,0 +1,239 @@
+package Statistics;
+
+use vars qw(
+$MultiQueueStatus $MultiQueueDateFormat @MultiQueueQueueList $MultiQueueMaxRows $MultiQueueWeekends $MultiQueueLabelDateFormat
+$PerDayStatus $PerDayDateFormat $PerDayQueue $PerDayMaxRows $PerDayWeekends $PerDayLabelDateFormat $PerDayPeriod
+$DayOfWeekQueue
+@OpenStalledQueueList $OpenStalledWeekends
+$TimeToResolveDateFormat $TimeToResolveQueue $TimeToResolveMaxRows $TimeToResolveWeekends $TimeToResolveLabelDateFormat
+$TimeToResolveGraphQueue
+@years @months %monthsMaxDay
+$secsPerDay
+$RestrictAccess
+$GraphWidth $GraphHeight
+);
+
+use Time::Local;
+
+# I couldn't figure out a way to override these in RT_SiteConfig, which would be
+# preferable.
+
+# Width and Height of all graphics
+$GraphWidth=500;
+$GraphHeight=400;
+
+# Initial settings for the CallsMultiQueue stat page
+$MultiQueueStatus = "resolved";
+$MultiQueueDateFormat = "%a %b %d %Y"; # format for dates on Multi Queue report, see "man strftime" for options
+@MultiQueueQueueList = ("General"); # list of queues to start Multi Queue per day reports
+$MultiQueueMaxRows = 10;
+$MultiQueueWeekends = 1;
+$MultiQueueLabelDateFormat = "%a";
+
+# Initial settings for the CallsQueueDay stat page
+$PerDayStatus = "resolved";
+$PerDayDateFormat = "%a %b %d %Y";
+$PerDayQueue = "General";
+$PerDayMaxRows = 10;
+$PerDayWeekends = 1;
+$PerDayLabelDateFormat = "%a";
+$PerDayPeriod = 10;
+
+# Initial settings for the DayOfWeek stat page
+$DayOfWeekQueue = "General";
+
+# Initial settings for the OpenStalled stat page
+@OpenStalledQueueList = ("General");
+$OpenStalledWeekends = 1;
+
+# Initial settings for the TimeToResolve stat page
+$TimeToResolveDateFormat = "%a %b %d";
+$TimeToResolveQueue = "General";
+$TimeToResolveMaxRows = 10;
+$TimeToResolveWeekends = 1;
+$TimeToResolveLabelDateFormat = "%a";
+
+# Initial settings for the TimeToResolve Graph page
+$TimeToResolveGraphQueue = "General";
+
+$secsPerDay = 86400;
+
+# List of years and months to populate drop down lists
+@years =('2010', '2009', '2008', '2007', '2006', '2005', '2004', '2003' ,'2003' ,'2002');
+@months=qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
+%monthsMaxDay = (
+ 0 => 31, # January
+ 1 => 29, # February, allow for leap year
+ 2 => 31, # March
+ 3 => 30, # April
+ 4 => 31, # May
+ 5 => 30, # June
+ 6 => 31, # July
+ 7 => 31, # August
+ 8 => 30, # September
+ 9 => 31, # October
+ 10=> 30, # November
+ 11=> 31 # December
+ );
+
+# Set to one to prevent users without the ShowConfigTab right from seeing Statistics
+$RestrictAccess = 0;
+
+# Variables to control debugging
+my $debugging=0; # set to 1 to enable debugging
+my $debugtext="";
+
+=head2 FormatDate
+
+Returns a string representing the specified date formatted by the specified string
+
+=cut
+sub FormatDate {
+ my $fmt = shift;
+ my $self = shift;
+ return POSIX::strftime($fmt, localtime($self->Unix));
+}
+
+
+=head2 RTDateSetToLocalMidnight
+
+Sets the date to midnight (at the beginning of the day) local time
+Returns the unixtime at midnight.
+
+=cut
+sub RTDateSetToLocalMidnight {
+ my $self = shift;
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($self->Unix);
+ $self->Unix(timelocal (0,0,0,$mday,$mon,$year,$wday,$yday));
+
+ return ($self->Unix);
+}
+
+=head2 RTDateIsWeekend
+
+Returns 1 if the date is on saturday or sunday
+
+=cut
+sub RTDateIsWeekend {
+ my $self = shift;
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($self->Unix);
+ return 1 if (($wday==6) || ($wday==0));
+ 0;
+}
+
+=head2 RTDateGetDateWeekday
+
+Returns the localized name of the day specified by date
+
+=cut
+sub RTDateGetDateWeekday {
+ my $self = shift;
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($self->Unix);
+ return $self->GetWeekday($wday);
+}
+
+=head2 RTDateSubDay
+
+Subtracts 24 hours from the current time
+
+=cut
+
+sub RTDateSubDay {
+ my $self = shift;
+ $self->AddSeconds(0 - $DAY);
+}
+
+=head2 RTDateSubDays $DAYS
+
+Subtracts 24 hours * $DAYS from the current time
+
+=cut
+
+sub RTDateSubDays {
+ my $self = shift;
+ my $days = shift;
+ $self->AddSeconds(0 - ($days * $DAY));
+}
+
+=head2 DebugInit
+
+Creates a text area on the page if debugging is on.
+
+=cut
+
+sub DebugInit {
+ if($debugging) {
+ my $m = shift;
+ $m->print("<TEXTAREA NAME=debugarea COLS=120 ROWS=50>$debugtext</TEXTAREA>\n");
+ }
+}
+
+=head2 DebugLog $logmsg
+
+Adds a message to the debug area
+
+=cut
+
+sub DebugLog {
+ if($debugging) {
+ my $line = shift;
+ $debugtext .= $line;
+ $RT::Logger->debug($line);
+ }
+}
+
+=head2 DebugClear
+
+Clears the current debug string, otherwise it builds from page to page
+
+=cut
+
+sub DebugClear {
+ if($debugging) {
+ $debugtext = undef;
+ }
+}
+
+=head2 DurationAsString
+
+Returns a string representing the specified duration
+
+=cut
+
+sub DurationAsString {
+ my $Duration = shift;
+ my $MINUTE = 60;
+ my $HOUR = $MINUTE*60;
+ my $DAY = $HOUR * 24;
+ my $WEEK = $DAY * 7;
+ my $days = int($Duration / $DAY);
+ $Duration = $Duration % $DAY;
+ my $hours = int($Duration / $HOUR);
+ $hours = sprintf("%02d", $hours);
+ $Duration = $Duration % $HOUR;
+ my $minutes = int($Duration/$MINUTE);
+ $minutes = sprintf("%02d", $minutes);
+ $Duration = $Duration % $MINUTE;
+ my $secs = sprintf("%02d", $Duration);
+
+ if(!$days) {
+ $days = "00";
+ }
+ if(!$hours) {
+ $hours = "00";
+ }
+ if(!$minutes) {
+ $minutes = "00";
+ }
+ if(!$secs) {
+ $secs = "00";
+ }
+ return "$days days $hours:$minutes:$secs";
+}
+
+1;
+
+
diff --git a/rt/lib/RTx/WebCronTool.pm b/rt/lib/RTx/WebCronTool.pm
new file mode 100644
index 000000000..5f086a279
--- /dev/null
+++ b/rt/lib/RTx/WebCronTool.pm
@@ -0,0 +1,41 @@
+package RTx::WebCronTool;
+$RTx::WebCronTool::VERSION = "0.01";
+
+1;
+
+__END__
+
+=head1 NAME
+
+RTx::WebCronTool - Web interface to rt-crontool
+
+=head1 VERSION
+
+This document describes version 0.01 of RTx::WebCronTool, released
+July 11, 2004.
+
+=head1 DESCRIPTION
+
+This RT extension provides a web interface for the built-in F<rt-crontool>
+utility, allowing scheduled processes to be launched remotely.
+
+After installation, log in as superuser, and click on the "Web CronTool" menu
+on the bottom of the navigation pane.
+
+To use it, simply submit the modules and arguments. All progress, error messages
+and debug information will then be displayed online.
+
+=head1 AUTHORS
+
+Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>
+
+=head1 COPYRIGHT
+
+Copyright 2004 by Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See L<http://www.perl.com/perl/misc/Artistic.html>
+
+=cut
diff --git a/rt/lib/t/00smoke.t.in b/rt/lib/t/00smoke.t.in
deleted file mode 100644
index 288dd4aae..000000000
--- a/rt/lib/t/00smoke.t.in
+++ /dev/null
@@ -1,14 +0,0 @@
-#!@PERL@
-
-use Test::More qw(no_plan);
-
-use lib "@RT_LIB_PATH@";
-use RT;
-ok(RT::LoadConfig);
-ok(RT::Init, "Basic initialization and DB connectivity");
-
-use File::Find;
-File::Find::find({wanted => \&wanted}, 'lib/');
-sub wanted { /^*\.pm\z/s && ok(require $_, "Requiring '$_'"); }
-
-
diff --git a/rt/lib/t/01harness.t.in b/rt/lib/t/01harness.t.in
deleted file mode 100644
index d132330c2..000000000
--- a/rt/lib/t/01harness.t.in
+++ /dev/null
@@ -1,12 +0,0 @@
-#!@PERL@
-
-use Test::More qw(no_plan);
-
-use lib "@RT_LIB_PATH@";
-use RT;
-ok(RT::LoadConfig);
-ok(RT::Init, "Basic initialization and DB connectivity");
-
-my $test = shift @ARGV;
-require $test;
-
diff --git a/rt/lib/t/02regression.t b/rt/lib/t/02regression.t
index 4504cc76a..4cc131815 100644
--- a/rt/lib/t/02regression.t
+++ b/rt/lib/t/02regression.t
@@ -34,11 +34,14 @@ is($q2->CommentAddress, 'comment@a');
use File::Find;
-File::Find::find({wanted => \&wanted_autogen}, 'lib/t/autogen');
+File::Find::find({wanted => \&wanted_autogen,
+ preprocess => sub {return sort @_}}, 'lib/t/autogen');
sub wanted_autogen { /^autogen.*\.t\z/s && require $_; }
-File::Find::find({wanted => \&wanted_regression}, 'lib/t/regression');
+File::Find::find({wanted => \&wanted_regression,
+ preprocess => sub {return sort @_}}, 'lib/t/regression');
sub wanted_regression { /^*\.t\z/s && require $_; }
require "/opt/rt3/lib/t/03web.pl";
require "/opt/rt3/lib/t/04_send_email.pl";
+require "/opt/rt3/lib/t/05cronsupport.pl";
diff --git a/rt/lib/t/02regression.t.in b/rt/lib/t/02regression.t.in
deleted file mode 100644
index c2e3277a9..000000000
--- a/rt/lib/t/02regression.t.in
+++ /dev/null
@@ -1,47 +0,0 @@
-#!@PERL@
-
-use Test::More qw(no_plan);
-
-use lib "@RT_LIB_PATH@";
-use RT;
-ok(RT::LoadConfig);
-ok(RT::Init, "Basic initialization and DB connectivity");
-
-# Create a new queue
-use_ok(RT::Queue);
-my $q = RT::Queue->new($RT::SystemUser);
-
-$q->Load('regression');
-if ($q->id != 0) {
- die "Regression tests not starting with a clean DB. Bailing";
-}
-
-my ($id, $msg) = $q->Create( Name => 'Regression',
- Description => 'A regression test queue',
- CorrespondAddress => 'correspond@a',
- CommentAddress => 'comment@a');
-
-isnt($id, 0, "Queue was created sucessfully - $msg");
-
-my $q2 = RT::Queue->new($RT::SystemUser);
-
-ok($q2->Load($id));
-is($q2->id, $id, "Sucessfully loaded the queue again");
-is($q2->Name, 'Regression');
-is($q2->Description, 'A regression test queue');
-is($q2->CorrespondAddress, 'correspond@a');
-is($q2->CommentAddress, 'comment@a');
-
-
-use File::Find;
-File::Find::find({wanted => \&wanted_autogen,
- preprocess => sub {return sort @_}}, 'lib/t/autogen');
-sub wanted_autogen { /^autogen.*\.t\z/s && require $_; }
-
-File::Find::find({wanted => \&wanted_regression,
- preprocess => sub {return sort @_}}, 'lib/t/regression');
-sub wanted_regression { /^*\.t\z/s && require $_; }
-
-require "@RT_LIB_PATH@/t/03web.pl";
-require "@RT_LIB_PATH@/t/04_send_email.pl";
-require "@RT_LIB_PATH@/t/05cronsupport.pl";
diff --git a/rt/lib/t/03web.pl b/rt/lib/t/03web.pl
index 94ad3e97e..597ad109e 100644
--- a/rt/lib/t/03web.pl
+++ b/rt/lib/t/03web.pl
@@ -67,7 +67,83 @@ ok( $agent->{'content'} =~ qr{$string} , "Found the content");
# }}}
+# {{{ Query Builder tests
+
+my $response = $agent->get($url."Search/Build.html");
+ok( $response->is_success, "Fetched " . $url."Search/Build.html" );
+
+# Parsing TicketSQL
+#
+# Adding items
+
+# set the first value
+ok($agent->form_name('BuildQuery'));
+$agent->field("AttachmentField", "Subject");
+$agent->field("AttachmentOp", "LIKE");
+$agent->field("ValueOfAttachment", "aaa");
+$agent->submit();
+
+# set the next value
+ok($agent->form_name('BuildQuery'));
+$agent->field("AttachmentField", "Subject");
+$agent->field("AttachmentOp", "LIKE");
+$agent->field("ValueOfAttachment", "bbb");
+$agent->submit();
+
+ok($agent->form_name('BuildQuery'));
+
+# get the query
+my $query = $agent->current_form->find_input("Query")->value;
+# strip whitespace from ends
+$query =~ s/^\s*//g;
+$query =~ s/\s*$//g;
+
+# collapse other whitespace
+$query =~ s/\s+/ /g;
+
+is ($query, "Subject LIKE 'aaa' AND Subject LIKE 'bbb'");
+
+# - new items go one level down
+# - add items at currently selected level
+# - if nothing is selected, add at end, one level down
+#
+# move left
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move left if you're at the top level
+#
+# move right
+# - error if nothing selected
+# - same item should be selected after move
+# - can always move right (no max depth...should there be?)
+#
+# move up
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move up if you're first in the list
+#
+# move down
+# - error if nothing selected
+# - same item should be selected after move
+# - can't move down if you're last in the list
+#
+# toggle
+# - error if nothing selected
+# - change all aggregators in the grouping
+# - don't change any others
+#
+# delete
+# - error if nothing selected
+# - delete currently selected item
+# - delete all children of a grouping
+# - if delete leaves a node with no children, delete that, too
+# - what should be selected?
+#
+# Clear
+# - clears entire query
+# - clears it from the session, too
+# }}}
use File::Find;
find ( \&wanted , 'html/');
@@ -83,7 +159,7 @@ sub test_get {
$file =~ s#^html/##;
ok ($agent->get("$url/$file", "GET $url/$file"));
is ($agent->{'status'}, 200, "Loaded $file");
- ok( $agent->{'content'} =~ /Logout/i, "Found a logout link on $file ");
+# ok( $agent->{'content'} =~ /Logout/i, "Found a logout link on $file ");
ok( $agent->{'content'} !~ /Not logged in/i, "Still logged in for $file");
ok( $agent->{'content'} !~ /System error/i, "Didn't get a Mason compilation error on $file");
diff --git a/rt/lib/t/03web.pl.in b/rt/lib/t/03web.pl.in
deleted file mode 100644
index 25c26e711..000000000
--- a/rt/lib/t/03web.pl.in
+++ /dev/null
@@ -1,170 +0,0 @@
-#!@PERL@
-
-use strict;
-use WWW::Mechanize;
-use HTTP::Request::Common;
-use HTTP::Cookies;
-use LWP;
-use Encode;
-
-my $cookie_jar = HTTP::Cookies->new;
-my $agent = WWW::Mechanize->new();
-
-# give the agent a place to stash the cookies
-
-$agent->cookie_jar($cookie_jar);
-
-
-# get the top page
-my $url = "http://localhost".$RT::WebPath."/";
-$agent->get($url);
-
-is ($agent->{'status'}, 200, "Loaded a page");
-
-
-# {{{ test a login
-
-# follow the link marked "Login"
-
-ok($agent->{form}->find_input('user'));
-
-ok($agent->{form}->find_input('pass'));
-ok ($agent->{'content'} =~ /username:/i);
-$agent->field( 'user' => 'root' );
-$agent->field( 'pass' => 'password' );
-# the field isn't named, so we have to click link 0
-$agent->click(0);
-is($agent->{'status'}, 200, "Fetched the page ok");
-ok( $agent->{'content'} =~ /Logout/i, "Found a logout link");
-
-
-
-$agent->get($url."Ticket/Create.html?Queue=1");
-is ($agent->{'status'}, 200, "Loaded Create.html");
-$agent->form(3);
-# Start with a string containing characters in latin1
-my $string = "I18N Web Testing æøå";
-Encode::from_to($string, 'iso-8859-1', 'utf8');
-$agent->field('Subject' => "Foo");
-$agent->field('Content' => $string);
-ok($agent->submit(), "Created new ticket with $string");
-
-ok( $agent->{'content'} =~ qr{$string} , "Found the content");
-
-$agent->get($url."Ticket/Create.html?Queue=1");
-is ($agent->{'status'}, 200, "Loaded Create.html");
-$agent->form(3);
-# Start with a string containing characters in latin1
-my $string = "I18N Web Testing æøå";
-Encode::from_to($string, 'iso-8859-1', 'utf8');
-$agent->field('Subject' => $string);
-$agent->field('Content' => "BAR");
-ok($agent->submit(), "Created new ticket with $string");
-
-ok( $agent->{'content'} =~ qr{$string} , "Found the content");
-
-
-
-# }}}
-
-# {{{ Query Builder tests
-
-my $response = $agent->get($url."Search/Build.html");
-ok( $response->is_success, "Fetched " . $url."Search/Build.html" );
-
-# Parsing TicketSQL
-#
-# Adding items
-
-# set the first value
-ok($agent->form_name('BuildQuery'));
-$agent->field("AttachmentField", "Subject");
-$agent->field("AttachmentOp", "LIKE");
-$agent->field("ValueOfAttachment", "aaa");
-$agent->submit();
-
-# set the next value
-ok($agent->form_name('BuildQuery'));
-$agent->field("AttachmentField", "Subject");
-$agent->field("AttachmentOp", "LIKE");
-$agent->field("ValueOfAttachment", "bbb");
-$agent->submit();
-
-ok($agent->form_name('BuildQuery'));
-
-# get the query
-my $query = $agent->current_form->find_input("Query")->value;
-# strip whitespace from ends
-$query =~ s/^\s*//g;
-$query =~ s/\s*$//g;
-
-# collapse other whitespace
-$query =~ s/\s+/ /g;
-
-is ($query, "Subject LIKE 'aaa' AND Subject LIKE 'bbb'");
-
-# - new items go one level down
-# - add items at currently selected level
-# - if nothing is selected, add at end, one level down
-#
-# move left
-# - error if nothing selected
-# - same item should be selected after move
-# - can't move left if you're at the top level
-#
-# move right
-# - error if nothing selected
-# - same item should be selected after move
-# - can always move right (no max depth...should there be?)
-#
-# move up
-# - error if nothing selected
-# - same item should be selected after move
-# - can't move up if you're first in the list
-#
-# move down
-# - error if nothing selected
-# - same item should be selected after move
-# - can't move down if you're last in the list
-#
-# toggle
-# - error if nothing selected
-# - change all aggregators in the grouping
-# - don't change any others
-#
-# delete
-# - error if nothing selected
-# - delete currently selected item
-# - delete all children of a grouping
-# - if delete leaves a node with no children, delete that, too
-# - what should be selected?
-#
-# Clear
-# - clears entire query
-# - clears it from the session, too
-
-# }}}
-
-use File::Find;
-find ( \&wanted , 'html/');
-
-sub wanted {
- -f && /\.html$/ && $_ !~ /Logout.html$/ && test_get($File::Find::name);
-}
-
-sub test_get {
- my $file = shift;
-
-
- $file =~ s#^html/##;
- ok ($agent->get("$url/$file", "GET $url/$file"));
- is ($agent->{'status'}, 200, "Loaded $file");
-# ok( $agent->{'content'} =~ /Logout/i, "Found a logout link on $file ");
- ok( $agent->{'content'} !~ /Not logged in/i, "Still logged in for $file");
- ok( $agent->{'content'} !~ /System error/i, "Didn't get a Mason compilation error on $file");
-
-}
-
-# }}}
-
-1;
diff --git a/rt/lib/t/04_send_email.pl b/rt/lib/t/04_send_email.pl
index c384eedfa..973d9d2e2 100644
--- a/rt/lib/t/04_send_email.pl
+++ b/rt/lib/t/04_send_email.pl
@@ -476,6 +476,31 @@ sub crashes_redef_sendmessage {
# }}}
+# {{{ test a multi-line RT-Send-CC header
+
+my $content = `cat /opt/rt3/lib/t/data/rt-send-cc` || die "couldn't find new content";
+
+$parser->ParseMIMEEntityFromScalar($content);
+
+
+
+my %args = (message => $content, queue => 1, action => 'correspond');
+ RT::Interface::Email::Gateway(\%args);
+my $tickets = RT::Tickets->new($RT::SystemUser);
+$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
+$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
+my $tick = $tickets->First();
+ok ($tick->Id, "found ticket ".$tick->Id);
+
+my $cc = $tick->Transactions->First->Attachments->First->GetHeader('RT-Send-Cc');
+ok ($cc =~ /test1/, "Found test 1");
+ok ($cc =~ /test2/, "Found test 2");
+ok ($cc =~ /test3/, "Found test 3");
+ok ($cc =~ /test4/, "Found test 4");
+ok ($cc =~ /test5/, "Found test 5");
+
+# }}}
+
# Don't taint the environment
$everyone->PrincipalObj->RevokeRight(Right =>'SuperUser');
1;
diff --git a/rt/lib/t/04_send_email.pl.in b/rt/lib/t/04_send_email.pl.in
deleted file mode 100644
index 39ab0d271..000000000
--- a/rt/lib/t/04_send_email.pl.in
+++ /dev/null
@@ -1,506 +0,0 @@
-#!@PERL@ -w
-
-use strict;
-use RT::EmailParser;
-use RT::Tickets;
-use RT::Action::SendEmail;
-
-my @_outgoing_messages;
-my @scrips_fired;
-
-#We're not testing acls here.
-my $everyone = RT::Group->new($RT::SystemUser);
-$everyone->LoadSystemInternalGroup('Everyone');
-$everyone->PrincipalObj->GrantRight(Right =>'SuperUser');
-
-
-is (__PACKAGE__, 'main', "We're operating in the main package");
-
-
-{
-no warnings qw/redefine/;
-sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
-
- main::_fired_scrip($self->ScripObj);
- main::ok(ref($MIME) eq 'MIME::Entity', "hey, look. it's a mime entity");
-}
-
-}
-
-# instrument SendEmail to pass us what it's about to send.
-# create a regular ticket
-
-my $parser = RT::EmailParser->new();
-
-
-# Let's test to make sure a multipart/report is processed correctly
-my $content = `cat @RT_LIB_PATH@/t/data/multipart-report` || die "couldn't find new content";
-# be as much like the mail gateway as possible.
-use RT::Interface::Email;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /The original message was received/, "It's the bounce");
-
-
-# make sure it fires scrips.
-is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
-
-undef @scrips_fired;
-
-
-
-
-$parser->ParseMIMEEntityFromScalar('From: root@localhost
-To: rt@example.com
-Subject: This is a test of new ticket creation as an unknown user
-
-Blah!
-Foob!');
-
-
-use Data::Dumper;
-
-my $ticket = RT::Ticket->new($RT::SystemUser);
-my ($id, $tid, $msg ) = $ticket->Create(Requestor => ['root@localhost'], Queue => 'general', Subject => 'I18NTest', MIMEObj => $parser->Entity);
-ok ($id,$msg);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-ok ($tick->Subject eq 'I18NTest', "failed to create the new ticket from an unprivileged account");
-
-# make sure it fires scrips.
-is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
-# make sure it sends an autoreply
-# make sure it sends a notification to adminccs
-
-
-# we need to swap out SendMessage to test the new things we care about;
-&utf8_redef_sendmessage;
-
-# create an iso 8859-1 ticket
-@scrips_fired = ();
-
-my $content = `cat @RT_LIB_PATH@/t/data/new-ticket-from-iso-8859-1` || die "couldn't find new content";
-
-
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-use RT::Interface::Email;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /H\x{e5}vard/, "It's signed by havard. yay");
-
-
-# make sure it fires scrips.
-is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
-# make sure it sends an autoreply
-
-
-# make sure it sends a notification to adminccs
-
-# If we correspond, does it do the right thing to the outbound messages?
-
-$parser->ParseMIMEEntityFromScalar($content);
-my ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity);
-ok ($id, $msg);
-
-$parser->ParseMIMEEntityFromScalar($content);
-($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity);
-ok ($id, $msg);
-
-
-
-
-
-# we need to swap out SendMessage to test the new things we care about;
-&iso8859_redef_sendmessage;
-$RT::EmailOutputEncoding = 'iso-8859-1';
-# create an iso 8859-1 ticket
-@scrips_fired = ();
-
-my $content = `cat @RT_LIB_PATH@/t/data/new-ticket-from-iso-8859-1` || die "couldn't find new content";
-# be as much like the mail gateway as possible.
-use RT::Interface::Email;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /H\x{e5}vard/, "It's signed by havard. yay");
-
-
-# make sure it fires scrips.
-is ($#scrips_fired, 1, "Fired 2 scrips on ticket creation");
-# make sure it sends an autoreply
-
-
-# make sure it sends a notification to adminccs
-
-
-# If we correspond, does it do the right thing to the outbound messages?
-
-$parser->ParseMIMEEntityFromScalar($content);
-my ($id, $msg) = $tick->Comment(MIMEObj => $parser->Entity);
-ok ($id, $msg);
-
-$parser->ParseMIMEEntityFromScalar($content);
-($id, $msg) = $tick->Correspond(MIMEObj => $parser->Entity);
-ok ($id, $msg);
-
-
-sub _fired_scrip {
- my $scrip = shift;
- push @scrips_fired, $scrip;
-}
-
-sub utf8_redef_sendmessage {
- no warnings qw/redefine/;
- eval '
- sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
-
- my $scrip = $self->ScripObj->id;
- ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name);
- main::_fired_scrip($self->ScripObj);
- $MIME->make_singlepart;
- main::ok( ref($MIME) eq \'MIME::Entity\',
- "hey, look. it\'s a mime entity" );
- main::ok( ref( $MIME->head ) eq \'MIME::Head\',
- "its mime header is a mime header. yay" );
- main::ok( $MIME->head->get(\'Content-Type\') =~ /utf-8/,
- "Its content type is utf-8" );
- my $message_as_string = $MIME->bodyhandle->as_string();
- use Encode;
- $message_as_string = Encode::decode_utf8($message_as_string);
- main::ok(
- $message_as_string =~ /H\x{e5}vard/,
-"The message\'s content contains havard\'s name. this will fail if it\'s not utf8 out");
-
- }';
-}
-
-sub iso8859_redef_sendmessage {
- no warnings qw/redefine/;
- eval '
- sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
-
- my $scrip = $self->ScripObj->id;
- ok(1, $self->ScripObj->ConditionObj->Name . " ".$self->ScripObj->ActionObj->Name);
- main::_fired_scrip($self->ScripObj);
- $MIME->make_singlepart;
- main::ok( ref($MIME) eq \'MIME::Entity\',
- "hey, look. it\'s a mime entity" );
- main::ok( ref( $MIME->head ) eq \'MIME::Head\',
- "its mime header is a mime header. yay" );
- main::ok( $MIME->head->get(\'Content-Type\') =~ /iso-8859-1/,
- "Its content type is iso-8859-1 - " . $MIME->head->get("Content-Type") );
- my $message_as_string = $MIME->bodyhandle->as_string();
- use Encode;
- $message_as_string = Encode::decode("iso-8859-1",$message_as_string);
- main::ok(
- $message_as_string =~ /H\x{e5}vard/, "The message\'s content contains havard\'s name. this will fail if it\'s not utf8 out");
-
- }';
-}
-
-# {{{ test a multipart alternative containing a text-html part with an umlaut
-
-my $content = `cat @RT_LIB_PATH@/t/data/multipart-alternative-with-umlaut` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&umlauts_redef_sendmessage;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /causes Error/, "We recorded the content right as text-plain");
-is ($tick->Transactions->First->Attachments->Count , 3 , "Has three attachments, presumably a text-plain, a text-html and a multipart alternative");
-
-sub umlauts_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage { }';
-}
-
-# }}}
-
-# {{{ test a text-html message with an umlaut
-
-my $content = `cat @RT_LIB_PATH@/t/data/text-html-with-umlaut` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&text_html_umlauts_redef_sendmessage;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Attachments->First->Content =~ /causes Error/, "We recorded the content as containing 'causes error'");
-ok ($tick->Transactions->First->Attachments->First->ContentType =~ /text\/html/, "We recorded the content as text/html");
-ok ($tick->Transactions->First->Attachments->Count ==1 , "Has one attachment, presumably a text-html and a multipart alternative");
-
-sub text_html_umlauts_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
- use Data::Dumper;
- return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
- ok (is $MIME->parts, 2, "generated correspondence mime entityis composed of three parts");
- is ($MIME->head->mime_type , "multipart/mixed", "The first part is a multipart mixed". $MIME->head->mime_type);
- is ($MIME->parts(0)->head->mime_type , "text/plain", "The second part is a plain");
- is ($MIME->parts(1)->head->mime_type , "text/html", "The third part is an html ");
- }';
-}
-
-# }}}
-
-# {{{ test a text-html message with russian characters
-
-my $content = `cat @RT_LIB_PATH@/t/data/text-html-in-russian` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&text_html_russian_redef_sendmessage;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Attachments->First->ContentType =~ /text\/html/, "We recorded the content right as text-html");
-ok ($tick->Transactions->First->Attachments->Count ==1 , "Has one attachment, presumably a text-html and a multipart alternative");
-
-sub text_html_russian_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
- use Data::Dumper;
- return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
- ok (is $MIME->parts, 2, "generated correspondence mime entityis composed of three parts");
- is ($MIME->head->mime_type , "multipart/mixed", "The first part is a multipart mixed". $MIME->head->mime_type);
- is ($MIME->parts(0)->head->mime_type , "text/plain", "The second part is a plain");
- is ($MIME->parts(1)->head->mime_type , "text/html", "The third part is an html ");
- my $content_1251;
- $content_1251 = $MIME->parts(1)->bodyhandle->as_string();
- ok ($content_1251 =~ qr{Ó÷eáíûé Öeíòp "ÊÀÄÐÛ ÄÅËÎÂÎÃÎ ÌÈÐÀ" ïpèãëaøaeò ía òpeíèíã:},
-"Content matches drugim in codepage 1251" );
- }';
-}
-
-# }}}
-
-# {{{ test a message containing a russian subject and NO content type
-
-unshift (@RT::EmailInputEncodings, 'koi8-r');
-$RT::EmailOutputEncoding = 'koi8-r';
-my $content = `cat @RT_LIB_PATH@/t/data/russian-subject-no-content-type` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&text_plain_russian_redef_sendmessage;
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Attachments->First->ContentType =~ /text\/plain/, "We recorded the content type right");
-ok ($tick->Transactions->First->Attachments->Count ==1 , "Has one attachment, presumably a text-plain");
-is ($tick->Subject, "\x{442}\x{435}\x{441}\x{442} \x{442}\x{435}\x{441}\x{442}", "Recorded the subject right");
-sub text_plain_russian_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
- return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
- is ($MIME->head->mime_type , "text/plain", "The only part is text/plain ");
- my $subject = $MIME->head->get("subject");
- chomp($subject);
- #is( $subject , /^=\?KOI8-R\?B\?W2V4YW1wbGUuY39tICM3XSDUxdPUINTF09Q=\?=/ , "The $subject is encoded correctly");
- };
- ';
-}
-
-shift @RT::EmailInputEncodings;
-$RT::EmailOutputEncoding = 'utf-8';
-# }}}
-
-
-# {{{ test a message containing a nested RFC 822 message
-
-my $content = `cat @RT_LIB_PATH@/t/data/nested-rfc-822` || die "couldn't find new content";
-ok ($content, "Loaded nested-rfc-822 to test");
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&text_plain_nested_redef_sendmessage;
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-is ($tick->Subject, "[Jonas Liljegren] Re: [Para] Niv\x{e5}er?");
-ok ($tick->Transactions->First->Attachments->First->ContentType =~ /multipart\/mixed/, "We recorded the content type right");
-is ($tick->Transactions->First->Attachments->Count , 5 , "Has one attachment, presumably a text-plain and a message RFC 822 and another plain");
-sub text_plain_nested_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage {
- my $self = shift;
- my $MIME = shift;
- return (1) unless ($self->ScripObj->ScripActionObj->Name eq "Notify AdminCcs" );
- is ($MIME->head->mime_type , "multipart/mixed", "It is a mixed multipart");
- my $subject = $MIME->head->get("subject");
- $subject = MIME::Base64::decode_base64( $subject);
- chomp($subject);
- # TODO, why does this test fail
- #ok($subject =~ qr{Niv\x{e5}er}, "The subject matches the word - $subject");
- 1;
- }';
-}
-
-# }}}
-
-
-# {{{ test a multipart alternative containing a uuencoded mesage generated by lotus notes
-
-my $content = `cat @RT_LIB_PATH@/t/data/notes-uuencoded` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&notes_redef_sendmessage;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /from Lotus Notes/, "We recorded the content right");
-is ($tick->Transactions->First->Attachments->Count , 3 , "Has three attachments");
-
-sub notes_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage { }';
-}
-
-# }}}
-
-# {{{ test a multipart that crashes the file-based mime-parser works
-
-my $content = `cat @RT_LIB_PATH@/t/data/crashes-file-based-parser` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-# be as much like the mail gateway as possible.
-&crashes_redef_sendmessage;
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-ok ($tick->Transactions->First->Content =~ /FYI/, "We recorded the content right");
-is ($tick->Transactions->First->Attachments->Count , 5 , "Has three attachments");
-
-sub crashes_redef_sendmessage {
- no warnings qw/redefine/;
- eval 'sub RT::Action::SendEmail::SendMessage { }';
-}
-
-
-
-# }}}
-
-# {{{ test a multi-line RT-Send-CC header
-
-my $content = `cat @RT_LIB_PATH@/t/data/rt-send-cc` || die "couldn't find new content";
-
-$parser->ParseMIMEEntityFromScalar($content);
-
-
-
-my %args = (message => $content, queue => 1, action => 'correspond');
- RT::Interface::Email::Gateway(\%args);
-my $tickets = RT::Tickets->new($RT::SystemUser);
-$tickets->OrderBy(FIELD => 'id', ORDER => 'DESC');
-$tickets->Limit(FIELD => 'id' ,OPERATOR => '>', VALUE => '0');
-my $tick = $tickets->First();
-ok ($tick->Id, "found ticket ".$tick->Id);
-
-my $cc = $tick->Transactions->First->Attachments->First->GetHeader('RT-Send-Cc');
-ok ($cc =~ /test1/, "Found test 1");
-ok ($cc =~ /test2/, "Found test 2");
-ok ($cc =~ /test3/, "Found test 3");
-ok ($cc =~ /test4/, "Found test 4");
-ok ($cc =~ /test5/, "Found test 5");
-
-# }}}
-
-# Don't taint the environment
-$everyone->PrincipalObj->RevokeRight(Right =>'SuperUser');
-1;
diff --git a/rt/lib/t/05cronsupport.pl.in b/rt/lib/t/05cronsupport.pl.in
deleted file mode 100644
index a6b3d7451..000000000
--- a/rt/lib/t/05cronsupport.pl.in
+++ /dev/null
@@ -1,84 +0,0 @@
-#!@PERL@ -w
-
-use strict;
-
-### Set up some testing data. Test the testing data because why not?
-
-# Create a user with rights, a queue, and some tickets.
-my $user_obj = RT::User->new($RT::SystemUser);
-my ($ret, $msg) = $user_obj->LoadOrCreateByEmail('tara@example.com');
-ok($ret, 'record test user creation');
-$user_obj->SetName('tara');
-$user_obj->PrincipalObj->GrantRight(Right => 'SuperUser');
-my $CurrentUser = RT::CurrentUser->new('tara');
-
-# Create our template, which will be used for tests of RT::Action::Record*.
-
-my $template_content = 'RT-Send-Cc: tla@example.com
-RT-Send-Bcc: jesse@example.com
-
-This is a content string with no content.';
-
-my $template_obj = RT::Template->new($CurrentUser);
-$template_obj->Create(Queue => '0',
- Name => 'recordtest',
- Description => 'testing Record actions',
- Content => $template_content,
- );
-
-# Create a queue and some tickets.
-
-my $queue_obj = RT::Queue->new($CurrentUser);
-($ret, $msg) = $queue_obj->Create(Name => 'recordtest', Description => 'queue for Action::Record testing');
-ok($ret, 'record test queue creation');
-
-my $ticket1 = RT::Ticket->new($CurrentUser);
-my ($id, $tobj, $msg2) = $ticket1->Create(Queue => $queue_obj,
- Requestor => ['tara@example.com'],
- Subject => 'bork bork bork',
- Priority => 22,
- );
-ok($id, 'record test ticket creation 1');
-my $ticket2 = RT::Ticket->new($CurrentUser);
-($id, $tobj, $msg2) = $ticket2->Create(Queue => $queue_obj,
- Requestor => ['root@localhost'],
- Subject => 'hurdy gurdy'
- );
-ok($id, 'record test ticket creation 2');
-
-
-### OK. Have data, will travel.
-
-# First test the search.
-
-ok(require RT::Search::FromSQL, "Search::FromSQL loaded");
-my $ticketsqlstr = "Requestor.EmailAddress = '" . $CurrentUser->EmailAddress .
- "' AND Priority > '20'";
-my $search = RT::Search::FromSQL->new(Argument => $ticketsqlstr, TicketsObj => RT::Tickets->new($CurrentUser),
- );
-is(ref($search), 'RT::Search::FromSQL', "search created");
-ok($search->Prepare(), "fromsql search run");
-my $counter = 0;
-while(my $t = $search->TicketsObj->Next() ) {
- is($t->Id(), $ticket1->Id(), "fromsql search results 1");
- $counter++;
-}
-is ($counter, 1, "fromsql search results 2");
-
-# Right. Now test the actions.
-
-ok(require RT::Action::RecordComment);
-ok(require RT::Action::RecordCorrespondence);
-
-my ($comment_act, $correspond_act);
-ok($comment_act = RT::Action::RecordComment->new(TicketObj => $ticket1, TemplateObj => $template_obj, CurrentUser => $CurrentUser), "RecordComment created");
-ok($correspond_act = RT::Action::RecordCorrespondence->new(TicketObj => $ticket2, TemplateObj => $template_obj, CurrentUser => $CurrentUser), "RecordCorrespondence created");
-ok($comment_act->Prepare(), "Comment prepared");
-ok($correspond_act->Prepare(), "Correspond prepared");
-ok($comment_act->Commit(), "Comment committed");
-ok($correspond_act->Commit(), "Correspondence committed");
-
-# Now test for loop suppression.
-my ($trans, $desc, $transaction) = $ticket2->Comment(MIMEObj => $template_obj->MIMEObj);
-my $bogus_action = RT::Action::RecordComment->new(TicketObj => $ticket1, TemplateObj => $template_obj, TransactionObj => $transaction, CurrentUser => $CurrentUser);
-ok(!$bogus_action->Prepare(), "Comment aborted to prevent loop");
diff --git a/rt/lib/t/regression/00placeholder b/rt/lib/t/regression/00placeholder
deleted file mode 100644
index 0afc6045c..000000000
--- a/rt/lib/t/regression/00placeholder
+++ /dev/null
@@ -1 +0,0 @@
-1;
diff --git a/rt/sbin/rt-setup-database b/rt/sbin/rt-setup-database
deleted file mode 100644
index d4a256f07..000000000
--- a/rt/sbin/rt-setup-database
+++ /dev/null
@@ -1,474 +0,0 @@
-#!/usr/bin/perl
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-use strict;
-use warnings;
-
-use vars qw($Nobody $SystemUser $item);
-
-# fix lib paths, some may be relative
-BEGIN {
- require File::Spec;
- my @libs = ("lib", "local/lib");
- my $bin_path;
-
- for my $lib (@libs) {
- unless ( File::Spec->file_name_is_absolute($lib) ) {
- unless ($bin_path) {
- if ( File::Spec->file_name_is_absolute(__FILE__) ) {
- $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
- }
- else {
- require FindBin;
- no warnings "once";
- $bin_path = $FindBin::Bin;
- }
- }
- $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
- }
- unshift @INC, $lib;
- }
-
-}
-
-#This drags in RT's config.pm
-# We do it in a begin block because RT::Handle needs to know the type to do its
-# inheritance
-BEGIN {
- use RT;
- RT::LoadConfig();
- RT::InitClasses();
-}
-
-use Term::ReadKey;
-use Getopt::Long;
-
-$| = 1; # unbuffer all output.
-
-my %args;
-GetOptions(
- \%args,
- 'action=s',
- 'force', 'debug',
- 'dba=s', 'dba-password=s', 'prompt-for-dba-password',
- 'datafile=s', 'datadir=s'
-);
-
-unless ( $args{'action'} ) {
- help();
- exit(-1);
-}
-
-# check and setup @actions
-my @actions = grep $_, split /,/, $args{'action'};
-if ( @actions > 1 && $args{'datafile'} ) {
- print STDERR "You can not use --datafile option with multiple actions.\n";
- exit(-1);
-}
-foreach ( @actions ) {
- unless ( /^(?:init|create|drop|schema|acl|coredata|insert|upgrade)$/ ) {
- print STDERR "$0 called with an invalid --action parameter.\n";
- exit(-1);
- }
- if ( /^(?:init|drop|upgrade)$/ && @actions > 1 ) {
- print STDERR "You can not mix init, drop or upgrade action with any action.\n";
- exit(-1);
- }
-}
-
-# convert init to multiple actions
-my $init = 0;
-if ( $actions[0] eq 'init' ) {
- @actions = qw(create schema acl coredata insert);
- $init = 1;
-}
-
-# set options from environment
-foreach my $key(qw(Type Host Name User Password)) {
- next unless exists $ENV{ 'RT_DB_'. uc $key };
- print "Using Database$key from RT_DB_". uc($key) ." environment variable.\n";
- RT->Config->Set( "Database$key", $ENV{ 'RT_DB_'. uc $key });
-}
-
-my $db_type = RT->Config->Get('DatabaseType') || '';
-my $db_host = RT->Config->Get('DatabaseHost') || '';
-my $db_name = RT->Config->Get('DatabaseName') || '';
-my $db_user = RT->Config->Get('DatabaseUser') || '';
-my $db_pass = RT->Config->Get('DatabasePassword') || '';
-
-# load it here to get error immidiatly if DB type is not supported
-require RT::Handle;
-
-if ( $db_type eq 'SQLite' && !File::Spec->file_name_is_absolute($db_name) ) {
- $db_name = File::Spec->catfile($RT::VarPath, $db_name);
- RT->Config->Set( DatabaseName => $db_name );
-}
-
-my $dba_user = $args{'dba'} || $ENV{'RT_DBA_USER'} || $db_user || '';
-my $dba_pass = $args{'dba-password'} || $ENV{'RT_DBA_PASSWORD'};
-
-if ( !$args{force} && ( !defined $dba_pass || $args{'prompt-for-dba-password'} ) ) {
- $dba_pass = get_dba_password();
- chomp $dba_pass if defined($dba_pass);
-}
-
-print "Working with:\n"
- ."Type:\t$db_type\nHost:\t$db_host\nName:\t$db_name\n"
- ."User:\t$db_user\nDBA:\t$dba_user\n";
-
-foreach my $action ( @actions ) {
- no strict 'refs';
- my ($status, $msg) = *{ 'action_'. $action }{'CODE'}->( %args );
- error($action, $msg) unless $status;
- print $msg ."\n" if $msg;
- print "Done.\n";
-}
-
-sub action_create {
- my %args = @_;
- my $dbh = get_system_dbh();
- my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'pre' );
- return ($status, $msg) unless $status;
-
- print "Now creating a $db_type database $db_name for RT.\n";
- return RT::Handle->CreateDatabase( $dbh );
-}
-
-sub action_drop {
- my %args = @_;
-
- print "Dropping $db_type database $db_name.\n";
- unless ( $args{'force'} ) {
- print <<END;
-
-About to drop $db_type database $db_name on $db_host.
-WARNING: This will erase all data in $db_name.
-
-END
- exit(-2) unless _yesno();
- }
-
- my $dbh = get_system_dbh();
- return RT::Handle->DropDatabase( $dbh );
-}
-
-sub action_schema {
- my %args = @_;
- my $dbh = get_admin_dbh();
- my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'pre' );
- return ($status, $msg) unless $status;
-
- print "Now populating database schema.\n";
- return RT::Handle->InsertSchema( $dbh, $args{'datafile'} || $args{'datadir'} );
-}
-
-sub action_acl {
- my %args = @_;
- my $dbh = get_admin_dbh();
- my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'pre' );
- return ($status, $msg) unless $status;
-
- print "Now inserting database ACLs\n";
- return RT::Handle->InsertACL( $dbh, $args{'datafile'} || $args{'datadir'} );
-}
-
-sub action_coredata {
- my %args = @_;
- $RT::Handle = new RT::Handle;
- $RT::Handle->dbh( undef );
- RT::ConnectToDatabase();
- RT::InitLogging();
- my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'pre' );
- return ($status, $msg) unless $status;
-
- print "Now inserting RT core system objects\n";
- return $RT::Handle->InsertInitialData;
-}
-
-sub action_insert {
- my %args = @_;
- $RT::Handle = new RT::Handle;
- RT::Init();
- my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'pre' );
- return ($status, $msg) unless $status;
-
- print "Now inserting data\n";
- my $file = $args{'datafile'};
- $file = $RT::EtcPath . "/initialdata" if $init && !$file;
- $file ||= $args{'datadir'}."/content";
- return $RT::Handle->InsertData( $file );
-}
-
-sub action_upgrade {
- my %args = @_;
- my $base_dir = $args{'datadir'} || "./etc/upgrade";
- return (0, "Couldn't read dir '$base_dir' with upgrade data")
- unless -d $base_dir || -r _;
-
- my $upgrading_from = undef;
- do {
- if ( defined $upgrading_from ) {
- print "Doesn't match #.#.#: ";
- } else {
- print "Enter RT version you're upgrading from: ";
- }
- $upgrading_from = scalar <STDIN>;
- chomp $upgrading_from;
- $upgrading_from =~ s/\s+//g;
- } while $upgrading_from !~ /^\d+\.\d+\.\d+$/;
-
- my $upgrading_to = $RT::VERSION;
- return (0, "The current version $upgrading_to is lower than $upgrading_from")
- if RT::Handle::cmp_version( $upgrading_from, $upgrading_to ) > 0;
-
- return (1, "The version $upgrading_to you're upgrading to is up to date")
- if RT::Handle::cmp_version( $upgrading_from, $upgrading_to ) == 0;
-
- my @versions = get_versions_from_to($base_dir, $upgrading_from, $upgrading_to);
-
- return (1, "No DB changes between $upgrading_from and $upgrading_to")
- unless @versions;
-
- print "\nGoing to apply following upgrades:\n";
- print map "* $_\n", @versions;
-
- {
- my $custom_upgrading_to = undef;
- do {
- if ( defined $custom_upgrading_to ) {
- print "Doesn't match #.#.#: ";
- } else {
- print "\nEnter RT version if you want to stop upgrade at some point,\n";
- print " or leave it blank if you want apply above upgrades: ";
- }
- $custom_upgrading_to = scalar <STDIN>;
- chomp $custom_upgrading_to;
- $custom_upgrading_to =~ s/\s+//g;
- last unless $custom_upgrading_to;
- } while $custom_upgrading_to !~ /^\d+\.\d+\.\d+$/;
-
- if ( $custom_upgrading_to ) {
- return (
- 0, "The version you entered ($custom_upgrading_to) is lower than\n"
- ."version you're upgrading from ($upgrading_from)"
- ) if RT::Handle::cmp_version( $upgrading_from, $custom_upgrading_to ) > 0;
-
- return (1, "The version you're upgrading to is up to date")
- if RT::Handle::cmp_version( $upgrading_from, $custom_upgrading_to ) == 0;
-
- if ( RT::Handle::cmp_version( $RT::VERSION, $custom_upgrading_to ) < 0 ) {
- print "Version you entered is greater than installed ($RT::VERSION).\n";
- _yesno() or exit(-2);
- }
- # ok, checked everything no let's refresh list
- $upgrading_to = $custom_upgrading_to;
- @versions = get_versions_from_to($base_dir, $upgrading_from, $upgrading_to);
-
- return (1, "No DB changes between $upgrading_from and $upgrading_to")
- unless @versions;
-
- print "\nGoing to apply following upgrades:\n";
- print map "* $_\n", @versions;
- }
- }
-
- print "\nIT'S VERY IMPORTANT TO BACK UP BEFORE THIS STEP\n\n";
- _yesno() or exit(-2) unless $args{'force'};
-
- foreach my $v ( @versions ) {
- print "Processing $v\n";
- my %tmp = (%args, datadir => "$base_dir/$v", datafile => undef);
- if ( -e "$base_dir/$v/schema.$db_type" ) {
- action_schema( %tmp );
- }
- if ( -e "$base_dir/$v/acl.$db_type" ) {
- action_acl( %tmp );
- }
- if ( -e "$base_dir/$v/content" ) {
- action_insert( %tmp );
- }
- }
- return 1;
-}
-
-sub get_versions_from_to {
- my ($base_dir, $from, $to) = @_;
-
- opendir my $dh, $base_dir or die "couldn't open dir: $!";
- my @versions = grep -d "$base_dir/$_" && /\d+\.\d+\.\d+/, readdir $dh;
- closedir $dh;
-
- return
- grep RT::Handle::cmp_version($_, $to) <= 0,
- grep RT::Handle::cmp_version($_, $from) > 0,
- sort RT::Handle::cmp_version @versions;
-}
-
-sub error {
- my ($action, $msg) = @_;
- print STDERR "Couldn't finish '$action' step.\n\n";
- print STDERR "ERROR: $msg\n\n";
- exit(-1);
-}
-
-sub get_dba_password {
- print "In order to create or update your RT database,"
- . " this script needs to connect to your "
- . " $db_type instance on $db_host as $dba_user\n";
- print "Please specify that user's database password below. If the user has no database\n";
- print "password, just press return.\n\n";
- print "Password: ";
- ReadMode('noecho');
- my $password = ReadLine(0);
- ReadMode('normal');
- print "\n";
- return ($password);
-}
-
-=head2 get_system_dbh
-
-Returns L<DBI> database handle connected to B<system> with DBA credentials.
-
-See also L<RT::Handle/SystemDSN>.
-
-=cut
-
-sub get_system_dbh {
- return _get_dbh( RT::Handle->SystemDSN, $dba_user, $dba_pass );
-}
-
-sub get_admin_dbh {
- return _get_dbh( RT::Handle->DSN, $dba_user, $dba_pass );
-}
-
-=head2 get_rt_dbh [USER, PASSWORD]
-
-Returns L<DBI> database handle connected to RT database,
-you may specify credentials(USER and PASSWORD) to connect
-with. By default connects with credentials from RT config.
-
-=cut
-
-sub get_rt_dbh {
- return _get_dbh( RT::Handle->DSN, $db_user, $db_pass );
-}
-
-sub _get_dbh {
- my ($dsn, $user, $pass) = @_;
- my $dbh = DBI->connect(
- $dsn, $user, $pass,
- { RaiseError => 0, PrintError => 0 },
- );
- unless ( $dbh ) {
- my $msg = "Failed to connect to $dsn as user '$user': ". $DBI::errstr;
- if ( $args{'debug'} ) {
- require Carp; Carp::confess( $msg );
- } else {
- print STDERR $msg; exit -1;
- }
- }
- return $dbh;
-}
-
-sub _yesno {
- print "Proceed [y/N]:";
- my $x = scalar(<STDIN>);
- $x =~ /^y/i;
-}
-
-sub help {
-
- print <<EOF;
-
-$0: Set up RT's database
-
---action init Initialize the database. This is combination of
- multiple actions listed below. Create DB, schema,
- setup acl, insert core data and initial data.
-
- upgrade Apply all needed schema/acl/content updates (will ask
- for version to upgrade from)
-
- create Create the database.
-
- drop Drop the database.
- This will ERASE ALL YOUR DATA
-
- schema Initialize only the database schema
- To use a local or supplementary datafile, specify it
- using the '--datadir' option below.
-
- acl Initialize only the database ACLs
- To use a local or supplementary datafile, specify it
- using the '--datadir' option below.
-
- coredata Insert data into RT's database. This data is required
- for normal functioning of any RT instance.
-
- insert Insert data into RT's database.
- By default, will use RT's installation data.
- To use a local or supplementary datafile, specify it
- using the '--datafile' option below.
-
-Several actions can be combined using comma separated list.
-
---datafile /path/to/datafile
---datadir /path/to/ Used to specify a path to find the local
- database schema and acls to be installed.
-
-
---dba dba's username
---dba-password dba's password
---prompt-for-dba-password Ask for the database administrator's password interactively
-
-
-EOF
-
-}
-
-1;
diff --git a/rt/sbin/rt-setup-database.in b/rt/sbin/rt-setup-database.in
index a51076fee..4ed48d7a3 100644
--- a/rt/sbin/rt-setup-database.in
+++ b/rt/sbin/rt-setup-database.in
@@ -1,9 +1,9 @@
-#!@PERL@
+#!@PERL@ -w
# BEGIN BPS TAGGED BLOCK {{{
#
# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
+#
+# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
# <jesse@bestpractical.com>
#
# (Except where explicitly superseded by other copyright notices)
@@ -47,56 +47,41 @@
#
# END BPS TAGGED BLOCK }}}
use strict;
-use warnings;
-
-use vars qw($Nobody $SystemUser $item);
+use vars qw($PROMPT $VERSION $Handle $Nobody $SystemUser $item);
+use vars
+ qw(@Groups @Users @ACL @Queues @ScripActions @ScripConditions @Templates @CustomFields @Scrips @Attributes);
-# fix lib paths, some may be relative
-BEGIN {
- require File::Spec;
- my @libs = ("@RT_LIB_PATH@", "@LOCAL_LIB_PATH@");
- my $bin_path;
-
- for my $lib (@libs) {
- unless ( File::Spec->file_name_is_absolute($lib) ) {
- unless ($bin_path) {
- if ( File::Spec->file_name_is_absolute(__FILE__) ) {
- $bin_path = ( File::Spec->splitpath(__FILE__) )[1];
- }
- else {
- require FindBin;
- no warnings "once";
- $bin_path = $FindBin::Bin;
- }
- }
- $lib = File::Spec->catfile( $bin_path, File::Spec->updir, $lib );
- }
- unshift @INC, $lib;
- }
-
-}
+use lib "@LOCAL_LIB_PATH@";
+use lib "@RT_LIB_PATH@";
#This drags in RT's config.pm
# We do it in a begin block because RT::Handle needs to know the type to do its
# inheritance
-BEGIN {
- use RT;
- RT::LoadConfig();
- RT::InitClasses();
-}
-
+use RT;
+use Carp;
+use RT::User;
+use RT::CurrentUser;
+use RT::Template;
+use RT::ScripAction;
+use RT::ACE;
+use RT::Group;
+use RT::User;
+use RT::Queue;
+use RT::ScripCondition;
+use RT::CustomField;
+use RT::Scrip;
+
+RT::LoadConfig();
use Term::ReadKey;
use Getopt::Long;
-$| = 1; # unbuffer all output.
-
my %args;
+
GetOptions(
\%args,
- 'action=s',
- 'force', 'debug',
- 'dba=s', 'dba-password=s', 'prompt-for-dba-password',
- 'datafile=s', 'datadir=s'
+ 'prompt-for-dba-password', 'force', 'debug',
+ 'action=s', 'dba=s', 'dba-password=s', 'datafile=s',
+ 'datadir=s'
);
unless ( $args{'action'} ) {
@@ -104,362 +89,615 @@ unless ( $args{'action'} ) {
exit(-1);
}
-# check and setup @actions
-my @actions = grep $_, split /,/, $args{'action'};
-if ( @actions > 1 && $args{'datafile'} ) {
- print STDERR "You can not use --datafile option with multiple actions.\n";
- exit(-1);
-}
-foreach ( @actions ) {
- unless ( /^(?:init|create|drop|schema|acl|coredata|insert|upgrade)$/ ) {
- print STDERR "$0 called with an invalid --action parameter.\n";
- exit(-1);
- }
- if ( /^(?:init|drop|upgrade)$/ && @actions > 1 ) {
- print STDERR "You can not mix init, drop or upgrade action with any action.\n";
- exit(-1);
- }
-}
+$| = 1; #unbuffer that output.
-# convert init to multiple actions
-my $init = 0;
-if ( $actions[0] eq 'init' ) {
- @actions = qw(create schema acl coredata insert);
- $init = 1;
-}
+require RT::Handle;
+my $Handle = RT::Handle->new($RT::DatabaseType);
+$Handle->BuildDSN;
+my $dbh;
-# set options from environment
-foreach my $key(qw(Type Host Name User Password)) {
- next unless exists $ENV{ 'RT_DB_'. uc $key };
- print "Using Database$key from RT_DB_". uc($key) ." environment variable.\n";
- RT->Config->Set( "Database$key", $ENV{ 'RT_DB_'. uc $key });
+if ( $args{'prompt-for-dba-password'} ) {
+ $args{'dba-password'} = get_dba_password();
+ chomp( $args{'dba-password'} );
}
-my $db_type = RT->Config->Get('DatabaseType') || '';
-my $db_host = RT->Config->Get('DatabaseHost') || '';
-my $db_name = RT->Config->Get('DatabaseName') || '';
-my $db_user = RT->Config->Get('DatabaseUser') || '';
-my $db_pass = RT->Config->Get('DatabasePassword') || '';
+if ( $args{'action'} eq 'init' ) {
+ $dbh = DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} )
+ || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
+ print "Now creating a database for RT.\n";
+ if ( $RT::DatabaseType ne 'Oracle' || $args{'dba'} ne $RT::DatabaseUser ) {
+ create_db();
+ } else {
+ print "...skipped as ".$args{'dba'} ." is not " . $RT::DatabaseUser . " or we're working with Oracle.\n";
+ }
-# load it here to get error immidiatly if DB type is not supported
-require RT::Handle;
+ if ( $RT::DatabaseType eq "mysql" ) {
+ # Check which version we're running
+ my ($version) = $dbh->selectrow_hashref("show variables like 'version'")->{Value} =~ /^(\d\.\d+)/;
+ print "*** Warning: RT is unsupported on MySQL versions before 4.0.x\n" if $version < 4;
+
+ # MySQL must have InnoDB support
+ my $innodb = $dbh->selectrow_hashref("show variables like 'have_innodb'")->{Value};
+ if ( $innodb eq "NO" ) {
+ die "RT requires that MySQL be compiled with InnoDB table support.\n".
+ "See http://dev.mysql.com/doc/mysql/en/InnoDB.html\n";
+ } elsif ( $innodb eq "DISABLED" ) {
+ die "RT requires that MySQL InnoDB table support be enabled.\n".
+ ($version < 4
+ ? "Add 'innodb_data_file_path=ibdata1:10M:autoextend' to the [mysqld] section of my.cnf\n"
+ : "Remove the 'skip-innodb' line from your my.cnf file, restart MySQL, and try again.\n");
+ }
+ }
-if ( $db_type eq 'SQLite' && !File::Spec->file_name_is_absolute($db_name) ) {
- $db_name = File::Spec->catfile($RT::VarPath, $db_name);
- RT->Config->Set( DatabaseName => $db_name );
-}
+ # SQLite can't deal with the disconnect/reconnect
+ unless ( $RT::DatabaseType eq 'SQLite' ) {
-my $dba_user = $args{'dba'} || $ENV{'RT_DBA_USER'} || $db_user || '';
-my $dba_pass = $args{'dba-password'} || $ENV{'RT_DBA_PASSWORD'};
+ $dbh->disconnect;
-if ( !$args{force} && ( !defined $dba_pass || $args{'prompt-for-dba-password'} ) ) {
- $dba_pass = get_dba_password();
- chomp $dba_pass if defined($dba_pass);
+ if ( $RT::DatabaseType eq "Oracle" ) {
+ $RT::DatabasePassword = $RT::DatabasePassword; #Warning avidance
+ $dbh = DBI->connect( $Handle->DSN, ${RT::DatabaseUser}, ${RT::DatabasePassword} ) || die $DBI::errstr;
+ } else {
+ $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} ) || die $DBI::errstr;
+ }
+ }
+ print "Now populating database schema.\n";
+ insert_schema();
+ print "Now inserting database ACLs\n";
+ insert_acl() unless $RT::DatabaseType eq 'Oracle';
+ print "Now inserting RT core system objects\n";
+ insert_initial_data();
+ print "Now inserting RT data\n";
+ insert_data( $RT::EtcPath . "/initialdata" );
+}
+elsif ( $args{'action'} eq 'drop' ) {
+ unless ( $dbh =
+ DBI->connect( get_system_dsn(), $args{'dba'}, $args{'dba-password'} ) )
+ {
+ warn $DBI::errstr;
+ warn "Database doesn't appear to exist. Aborting database drop.";
+ exit;
+ }
+ drop_db();
+}
+elsif ( $args{'action'} eq 'insert_initial' ) {
+ insert_initial_data();
}
+elsif ( $args{'action'} eq 'insert' ) {
+ insert_data( $args{'datafile'} || ($args{'datadir'}."/content") );
+}
+elsif ( $args{'action'} eq 'acl' ) {
+ $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} )
+ || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
+ insert_acl($args{'datadir'});
+}
+elsif ( $args{'action'} eq 'schema' ) {
+ $dbh = DBI->connect( $Handle->DSN, $args{'dba'}, $args{'dba-password'} )
+ || die "Failed to connect to " . get_system_dsn() . " as $args{'dba'}: $DBI::errstr";
+ insert_schema($args{'datadir'});
+}
+else {
+ print STDERR "$0 called with an invalid --action parameter\n";
+ exit(-1);
+}
+
+# {{{ sub insert_schema
+sub insert_schema {
+ my $base_path = (shift || $RT::EtcPath);
+ my (@schema);
+ print "Creating database schema.\n";
+
+ my $schema_file = $base_path . "/schema." . $RT::DatabaseType;
+ if ( -f $schema_file ) {
+ open( SCHEMA, "<$schema_file" ) or die "Can't open $schema_file: $!";
+ my @lines = <SCHEMA>;
+
+ my $local_schema_file = $RT::LocalEtcPath . "/schema." . $RT::DatabaseType;
+ if (-f $local_schema_file) {
+ open( SCHEMA_LOCAL, "<$local_schema_file" )
+ or die "Can't open $local_schema_file: $!";
+ push @lines, ';;', <SCHEMA_LOCAL>;
+ }
+
+ my $statement = "";
+ foreach my $line (@lines) {
+ $line =~ s/\#.*//g;
+ $line =~ s/--.*//g;
+ $statement .= $line;
+ if ( $line =~ /;(\s*)$/ ) {
+ $statement =~ s/;(\s*)$//g;
+ push @schema, $statement;
+ $statement = "";
+ }
+ }
-print "Working with:\n"
- ."Type:\t$db_type\nHost:\t$db_host\nName:\t$db_name\n"
- ."User:\t$db_user\nDBA:\t$dba_user\n";
+ local $SIG{__WARN__} = sub {};
+ my $is_local = 0; # local/etc/schema needs to be nonfatal.
+ $dbh->begin_work or die $dbh->errstr;
+ foreach my $statement (@schema) {
+ if ( $statement =~ /^\s*;$/ ) { $is_local = 1; next; }
-foreach my $action ( @actions ) {
- no strict 'refs';
- my ($status, $msg) = *{ 'action_'. $action }{'CODE'}->( %args );
- error($action, $msg) unless $status;
- print $msg ."\n" if $msg;
- print "Done.\n";
+ print STDERR "SQL: $statement\n" if defined $args{'debug'};
+ my $sth = $dbh->prepare($statement) or die $dbh->errstr;
+ unless ( $sth->execute or $is_local ) {
+ die "Problem with statement:\n $statement\n" . $sth->errstr;
+ }
+ }
+ $dbh->commit or die $dbh->errstr;
+ }
+ else {
+ die "Couldn't find schema file for " . $RT::DatabaseType . "\n";
+ }
+ print "Done setting up database schema.\n";
}
-sub action_create {
- my %args = @_;
- my $dbh = get_system_dbh();
- my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'pre' );
- return ($status, $msg) unless $status;
+# }}}
- print "Now creating a $db_type database $db_name for RT.\n";
- return RT::Handle->CreateDatabase( $dbh );
-}
+# {{{ sub drop_db
+sub drop_db {
+ if ( $RT::DatabaseType eq 'Oracle' ) {
+ print <<END;
-sub action_drop {
- my %args = @_;
+To delete the tables and sequences of the RT Oracle database by running
+ \@etc/drop.Oracle
+through SQLPlus.
- print "Dropping $db_type database $db_name.\n";
+END
+ return;
+ }
unless ( $args{'force'} ) {
print <<END;
-About to drop $db_type database $db_name on $db_host.
-WARNING: This will erase all data in $db_name.
+About to drop $RT::DatabaseType database $RT::DatabaseName on $RT::DatabaseHost.
+WARNING: This will erase all data in $RT::DatabaseName.
END
- exit(-2) unless _yesno();
+ exit unless _yesno();
+
}
- my $dbh = get_system_dbh();
- return RT::Handle->DropDatabase( $dbh );
+ print "Dropping $RT::DatabaseType database $RT::DatabaseName.\n";
+
+ if ( $RT::DatabaseType eq 'SQLite' ) {
+ unlink $RT::DatabaseName or warn $!;
+ return;
+ }
+ $dbh->do("Drop DATABASE $RT::DatabaseName") or warn $DBI::errstr;
}
-sub action_schema {
- my %args = @_;
- my $dbh = get_admin_dbh();
- my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'pre' );
- return ($status, $msg) unless $status;
+# }}}
- print "Now populating database schema.\n";
- return RT::Handle->InsertSchema( $dbh, $args{'datafile'} || $args{'datadir'} );
+# {{{ sub create_db
+sub create_db {
+ print "Creating $RT::DatabaseType database $RT::DatabaseName.\n";
+ if ( $RT::DatabaseType eq 'SQLite' ) {
+ return;
+ }
+ elsif ( $RT::DatabaseType eq 'Pg' ) {
+ $dbh->do("CREATE DATABASE $RT::DatabaseName WITH ENCODING='UNICODE'");
+ if ( $DBI::errstr ) {
+ $dbh->do("CREATE DATABASE $RT::DatabaseName") || die $DBI::errstr;
+ }
+ }
+ elsif ( $RT::DatabaseType eq 'Oracle' ) {
+ insert_acl();
+ }
+ elsif ( $RT::DatabaseType eq 'Informix' ) {
+ $ENV{DB_LOCALE} = 'en_us.utf8';
+ $dbh->do("CREATE DATABASE $RT::DatabaseName WITH BUFFERED LOG");
+ }
+ else {
+ $dbh->do("CREATE DATABASE $RT::DatabaseName") or die $DBI::errstr;
+ }
}
-sub action_acl {
- my %args = @_;
- my $dbh = get_admin_dbh();
- my ($status, $msg) = RT::Handle->CheckCompatibility( $dbh, 'pre' );
- return ($status, $msg) unless $status;
+# }}}
- print "Now inserting database ACLs\n";
- return RT::Handle->InsertACL( $dbh, $args{'datafile'} || $args{'datadir'} );
+sub get_dba_password {
+ print "In order to create or update your RT database,";
+ print "this script needs to connect to your "
+ . $RT::DatabaseType
+ . " instance on "
+ . $RT::DatabaseHost . " as "
+ . $args{'dba'} . ".\n";
+ print "Please specify that user's database password below. If the user has no database\n";
+ print "password, just press return.\n\n";
+ print "Password: ";
+ ReadMode('noecho');
+ my $password = ReadLine(0);
+ ReadMode('normal');
+ print "\n";
+ return ($password);
}
-sub action_coredata {
- my %args = @_;
- $RT::Handle = new RT::Handle;
- $RT::Handle->dbh( undef );
- RT::ConnectToDatabase();
- RT::InitLogging();
- my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'pre' );
- return ($status, $msg) unless $status;
+# {{{ sub _yesno
+sub _yesno {
+ print "Proceed [y/N]:";
+ my $x = scalar(<STDIN>);
+ $x =~ /^y/i;
+}
- print "Now inserting RT core system objects\n";
- return $RT::Handle->InsertInitialData;
+# }}}
+
+# {{{ insert_acls
+sub insert_acl {
+ my $base_path = (shift || $RT::EtcPath);
+
+ if ( $RT::DatabaseType =~ /^oracle$/i ) {
+ do $base_path . "/acl.Oracle"
+ || die "Couldn't find ACLS for Oracle\n" . $@;
+ }
+ elsif ( $RT::DatabaseType =~ /^pg$/i ) {
+ do $base_path . "/acl.Pg" || die "Couldn't find ACLS for Pg\n" . $@;
+ }
+ elsif ( $RT::DatabaseType =~ /^mysql$/i ) {
+ do $base_path . "/acl.mysql"
+ || die "Couldn't find ACLS for mysql in $base_path\n" . $@;
+ }
+ elsif ( $RT::DatabaseType =~ /^Sybase$/i ) {
+ do $base_path . "/acl.Sybase"
+ || die "Couldn't find ACLS for Sybase in $base_path\n" . $@;
+ }
+ elsif ( $RT::DatabaseType =~ /^informix$/i ) {
+ do $base_path . "/acl.Informix"
+ || die "Couldn't find ACLS for Informix in $base_path\n" . $@;
+ }
+ elsif ( $RT::DatabaseType =~ /^SQLite$/i ) {
+ return;
+ }
+ else {
+ die "Unknown RT database type";
+ }
+
+ my @acl = acl($dbh);
+ foreach my $statement (@acl) {
+ print STDERR $statement if $args{'debug'};
+ my $sth = $dbh->prepare($statement) or die $dbh->errstr;
+ unless ( $sth->execute ) {
+ die "Problem with statement:\n $statement\n" . $sth->errstr;
+ }
+ }
+ print "Done setting up database ACLs.\n";
}
-sub action_insert {
- my %args = @_;
- $RT::Handle = new RT::Handle;
- RT::Init();
- my ($status, $msg) = RT::Handle->CheckCompatibility( $RT::Handle->dbh, 'pre' );
- return ($status, $msg) unless $status;
-
- print "Now inserting data\n";
- my $file = $args{'datafile'};
- $file = $RT::EtcPath . "/initialdata" if $init && !$file;
- $file ||= $args{'datadir'}."/content";
- return $RT::Handle->InsertData( $file );
+# }}}
+
+=head2 get_system_dsn
+
+Returns a dsn suitable for database creates and drops
+and user creates and drops
+
+=cut
+
+sub get_system_dsn {
+
+ my $dsn = $Handle->DSN;
+
+ #with mysql, you want to connect sans database to funge things
+ if ( $RT::DatabaseType eq 'mysql' ) {
+ $dsn =~ s/dbname=$RT::DatabaseName//;
+
+ # with postgres, you want to connect to database1
+ }
+ elsif ( $RT::DatabaseType eq 'Pg' ) {
+ $dsn =~ s/dbname=$RT::DatabaseName/dbname=template1/;
+ }
+ elsif ( $RT::DatabaseType eq 'Informix' ) {
+ # with Informix, you want to connect sans database:
+ $dsn =~ s/Informix:$RT::DatabaseName/Informix:/;
+ }
+ return $dsn;
}
-sub action_upgrade {
- my %args = @_;
- my $base_dir = $args{'datadir'} || "./etc/upgrade";
- return (0, "Couldn't read dir '$base_dir' with upgrade data")
- unless -d $base_dir || -r _;
-
- my $upgrading_from = undef;
- do {
- if ( defined $upgrading_from ) {
- print "Doesn't match #.#.#: ";
- } else {
- print "Enter RT version you're upgrading from: ";
- }
- $upgrading_from = scalar <STDIN>;
- chomp $upgrading_from;
- $upgrading_from =~ s/\s+//g;
- } while $upgrading_from !~ /^\d+\.\d+\.\d+$/;
+sub insert_initial_data {
- my $upgrading_to = $RT::VERSION;
- return (0, "The current version $upgrading_to is lower than $upgrading_from")
- if RT::Handle::cmp_version( $upgrading_from, $upgrading_to ) > 0;
+ RT::InitLogging();
- return (1, "The version $upgrading_to you're upgrading to is up to date")
- if RT::Handle::cmp_version( $upgrading_from, $upgrading_to ) == 0;
+ #connect to the db, for actual RT work
+ require RT::Handle;
+ $RT::Handle = RT::Handle->new();
+ $RT::Handle->Connect();
- my @versions = get_versions_from_to($base_dir, $upgrading_from, $upgrading_to);
+ #Put together a current user object so we can create a User object
+ my $CurrentUser = new RT::CurrentUser();
- return (1, "No DB changes between $upgrading_from and $upgrading_to")
- unless @versions;
+ print "Checking for existing system user...";
+ my $test_user = RT::User->new($CurrentUser);
+ $test_user->Load('RT_System');
+ if ( $test_user->id ) {
+ print "found!\n\nYou appear to have a functional RT database.\n"
+ . "Exiting, so as not to clobber your existing data.\n";
+ exit(-1);
- print "\nGoing to apply following upgrades:\n";
- print map "* $_\n", @versions;
+ }
+ else {
+ print "not found. This appears to be a new installation.\n";
+ }
- {
- my $custom_upgrading_to = undef;
- do {
- if ( defined $custom_upgrading_to ) {
- print "Doesn't match #.#.#: ";
- } else {
- print "\nEnter RT version if you want to stop upgrade at some point,\n";
- print " or leave it blank if you want apply above upgrades: ";
- }
- $custom_upgrading_to = scalar <STDIN>;
- chomp $custom_upgrading_to;
- $custom_upgrading_to =~ s/\s+//g;
- last unless $custom_upgrading_to;
- } while $custom_upgrading_to !~ /^\d+\.\d+\.\d+$/;
-
- if ( $custom_upgrading_to ) {
- return (
- 0, "The version you entered ($custom_upgrading_to) is lower than\n"
- ."version you're upgrading from ($upgrading_from)"
- ) if RT::Handle::cmp_version( $upgrading_from, $custom_upgrading_to ) > 0;
-
- return (1, "The version you're upgrading to is up to date")
- if RT::Handle::cmp_version( $upgrading_from, $custom_upgrading_to ) == 0;
-
- if ( RT::Handle::cmp_version( $RT::VERSION, $custom_upgrading_to ) < 0 ) {
- print "Version you entered is greater than installed ($RT::VERSION).\n";
- _yesno() or exit(-2);
- }
- # ok, checked everything no let's refresh list
- $upgrading_to = $custom_upgrading_to;
- @versions = get_versions_from_to($base_dir, $upgrading_from, $upgrading_to);
+ print "Creating system user...";
+ my $RT_System = new RT::User($CurrentUser);
- return (1, "No DB changes between $upgrading_from and $upgrading_to")
- unless @versions;
+ my ( $val, $msg ) = $RT_System->_BootstrapCreate(
+ Name => 'RT_System',
+ RealName => 'The RT System itself',
+ Comments =>
+'Do not delete or modify this user. It is integral to RT\'s internal database structures',
+ Creator => '1',
+ LastUpdatedBy => '1',
+ );
- print "\nGoing to apply following upgrades:\n";
- print map "* $_\n", @versions;
- }
+ unless ( $val ) {
+ print "$msg\n";
+ exit(-1);
}
+ print "done.\n";
+ $RT::Handle->Disconnect() unless $RT::DatabaseType eq 'SQLite';
+
+}
+
+# load some sort of data into the database
+
+sub insert_data {
+ my $datafile = shift;
+
+ #Connect to the database and get RT::SystemUser and RT::Nobody loaded
+ RT::Init;
+
+ my $CurrentUser = RT::CurrentUser->new();
+ $CurrentUser->LoadByName('RT_System');
+
+ if ( $datafile eq $RT::EtcPath . "/initialdata" ) {
- print "\nIT'S VERY IMPORTANT TO BACK UP BEFORE THIS STEP\n\n";
- _yesno() or exit(-2) unless $args{'force'};
+ print "Creating Superuser ACL...";
- foreach my $v ( @versions ) {
- print "Processing $v\n";
- my %tmp = (%args, datadir => "$base_dir/$v", datafile => undef);
- if ( -e "$base_dir/$v/schema.$db_type" ) {
- action_schema( %tmp );
+ my $superuser_ace = RT::ACE->new($CurrentUser);
+ $superuser_ace->_BootstrapCreate(
+ PrincipalId => ACLEquivGroupId( $CurrentUser->Id ),
+ PrincipalType => 'Group',
+ RightName => 'SuperUser',
+ ObjectType => 'RT::System',
+ ObjectId => '1' );
+
+ print "done.\n";
+ }
+
+ # Slurp in stuff to insert from the datafile. Possible things to go in here:-
+ # @groups, @users, @acl, @queues, @ScripActions, @ScripConditions, @templates
+
+ require $datafile
+ || die "Couldn't find initial data for import\n" . $@;
+
+ if ( @Groups ) {
+ print "Creating groups...";
+ foreach $item (@Groups) {
+ my $new_entry = RT::Group->new($CurrentUser);
+ my ( $return, $msg ) = $new_entry->_Create(%$item);
+ print "(Error: $msg)" unless $return;
+ print $return. ".";
}
- if ( -e "$base_dir/$v/acl.$db_type" ) {
- action_acl( %tmp );
+ print "done.\n";
+ }
+ if ( @Users ) {
+ print "Creating users...";
+ foreach $item (@Users) {
+ my $new_entry = new RT::User($CurrentUser);
+ my ( $return, $msg ) = $new_entry->Create(%$item);
+ print "(Error: $msg)" unless $return;
+ print $return. ".";
}
- if ( -e "$base_dir/$v/content" ) {
- action_insert( %tmp );
+ print "done.\n";
+ }
+ if ( @Queues ) {
+ print "Creating queues...";
+ for $item (@Queues) {
+ my $new_entry = new RT::Queue($CurrentUser);
+ my ( $return, $msg ) = $new_entry->Create(%$item);
+ print "(Error: $msg)" unless $return;
+ print $return. ".";
}
+ print "done.\n";
}
- return 1;
-}
+ if ( @ACL ) {
+ print "Creating ACL...";
+ for my $item (@ACL) {
-sub get_versions_from_to {
- my ($base_dir, $from, $to) = @_;
+ my ($princ, $object);
- opendir my $dh, $base_dir or die "couldn't open dir: $!";
- my @versions = grep -d "$base_dir/$_" && /\d+\.\d+\.\d+/, readdir $dh;
- closedir $dh;
+ # Global rights or Queue rights?
+ if ( $item->{'Queue'} ) {
+ $object = RT::Queue->new($CurrentUser);
+ $object->Load( $item->{'Queue'} );
+ } else {
+ $object = $RT::System;
+ }
- return
- grep RT::Handle::cmp_version($_, $to) <= 0,
- grep RT::Handle::cmp_version($_, $from) > 0,
- sort RT::Handle::cmp_version @versions;
-}
+ # Group rights or user rights?
+ if ( $item->{'GroupDomain'} ) {
+ $princ = RT::Group->new($CurrentUser);
+ if ( $item->{'GroupDomain'} eq 'UserDefined' ) {
+ $princ->LoadUserDefinedGroup( $item->{'GroupId'} );
+ } elsif ( $item->{'GroupDomain'} eq 'SystemInternal' ) {
+ $princ->LoadSystemInternalGroup( $item->{'GroupType'} );
+ } elsif ( $item->{'GroupDomain'} eq 'RT::System-Role' ) {
+ $princ->LoadSystemRoleGroup( $item->{'GroupType'} );
+ } elsif ( $item->{'GroupDomain'} eq 'RT::Queue-Role' &&
+ $item->{'Queue'} )
+ {
+ $princ->LoadQueueRoleGroup( Type => $item->{'GroupType'},
+ Queue => $object->id);
+ } else {
+ $princ->Load( $item->{'GroupId'} );
+ }
+ } else {
+ $princ = RT::User->new($CurrentUser);
+ $princ->Load( $item->{'UserId'} );
+ }
-sub error {
- my ($action, $msg) = @_;
- print STDERR "Couldn't finish '$action' step.\n\n";
- print STDERR "ERROR: $msg\n\n";
- exit(-1);
-}
+ # Grant it
+ my ( $return, $msg ) = $princ->PrincipalObj->GrantRight(
+ Right => $item->{'Right'},
+ Object => $object );
-sub get_dba_password {
- print "In order to create or update your RT database,"
- . " this script needs to connect to your "
- . " $db_type instance on $db_host as $dba_user\n";
- print "Please specify that user's database password below. If the user has no database\n";
- print "password, just press return.\n\n";
- print "Password: ";
- ReadMode('noecho');
- my $password = ReadLine(0);
- ReadMode('normal');
- print "\n";
- return ($password);
-}
+ if ( $return ) {
+ print $return. ".";
+ }
+ else {
+ print $msg . ".";
-=head2 get_system_dbh
+ }
-Returns L<DBI> database handle connected to B<system> with DBA credentials.
+ }
+ print "done.\n";
+ }
+ if ( @CustomFields ) {
+ print "Creating custom fields...";
+ for $item (@CustomFields) {
+ my $new_entry = new RT::CustomField($CurrentUser);
+ my $values = $item->{'Values'};
+ delete $item->{'Values'};
+ my ( $return, $msg ) = $new_entry->Create(%$item);
+ unless( $return ) {
+ print "(Error: $msg)\n";
+ next;
+ }
-See also L<RT::Handle/SystemDSN>.
+ foreach my $value ( @{$values} ) {
+ my ( $eval, $emsg ) = $new_entry->AddValue(%$value);
+ print "(Error: $emsg)\n" unless $eval;
+ }
-=cut
+ if ( $item->{LookupType} && !exists $item->{'Queue'} ) { # enable by default
+ my $ocf = RT::ObjectCustomField->new($CurrentUser);
+ $ocf->Create( CustomField => $new_entry->Id );
+ }
-sub get_system_dbh {
- return _get_dbh( RT::Handle->SystemDSN, $dba_user, $dba_pass );
-}
+ print "(Error: $msg)\n" unless $return;
+ print $return. ".";
+ }
-sub get_admin_dbh {
- return _get_dbh( RT::Handle->DSN, $dba_user, $dba_pass );
-}
+ print "done.\n";
+ }
-=head2 get_rt_dbh [USER, PASSWORD]
+ if ( @ScripActions ) {
+ print "Creating ScripActions...";
-Returns L<DBI> database handle connected to RT database,
-you may specify credentials(USER and PASSWORD) to connect
-with. By default connects with credentials from RT config.
+ for $item (@ScripActions) {
+ my $new_entry = RT::ScripAction->new($CurrentUser);
+ my ($return,$msg) = $new_entry->Create(%$item);
+ unless ($return) {
+ print "(Error: $msg)\n";
+ next;
+ }
+ print $return. ".";
+ }
-=cut
+ print "done.\n";
+ }
-sub get_rt_dbh {
- return _get_dbh( RT::Handle->DSN, $db_user, $db_pass );
-}
+ if ( @ScripConditions ) {
+ print "Creating ScripConditions...";
-sub _get_dbh {
- my ($dsn, $user, $pass) = @_;
- my $dbh = DBI->connect(
- $dsn, $user, $pass,
- { RaiseError => 0, PrintError => 0 },
- );
- unless ( $dbh ) {
- my $msg = "Failed to connect to $dsn as user '$user': ". $DBI::errstr;
- if ( $args{'debug'} ) {
- require Carp; Carp::confess( $msg );
- } else {
- print STDERR $msg; exit -1;
+ for $item (@ScripConditions) {
+ my $new_entry = RT::ScripCondition->new($CurrentUser);
+ my ($return,$msg) = $new_entry->Create(%$item);
+ unless ($return) {
+ print "(Error: $msg)\n";
+ next;
+ }
+ print $return. ".";
}
- }
- return $dbh;
-}
-sub _yesno {
- print "Proceed [y/N]:";
- my $x = scalar(<STDIN>);
- $x =~ /^y/i;
-}
+ print "done.\n";
+ }
-sub help {
+ if ( @Templates ) {
+ print "Creating templates...";
- print <<EOF;
+ for $item (@Templates) {
+ my $new_entry = new RT::Template($CurrentUser);
+ my $return = $new_entry->Create(%$item);
+ print $return. ".";
+ }
+ print "done.\n";
+ }
+ if ( @Scrips ) {
+ print "Creating scrips...";
+
+ for $item (@Scrips) {
+ my $new_entry = new RT::Scrip($CurrentUser);
+ my ( $return, $msg ) = $new_entry->Create(%$item);
+ if ( $return ) {
+ print $return. ".";
+ }
+ else {
+ print "(Error: $msg)\n";
+ }
+ }
+ print "done.\n";
+ }
+ if ( @Attributes ) {
+ print "Creating predefined searches...";
+ my $sys = RT::System->new($CurrentUser);
+
+ for $item (@Attributes) {
+ my $obj = delete $item->{Object}; # XXX: make this something loadable
+ $obj ||= $sys;
+ my ( $return, $msg ) = $obj->AddAttribute (%$item);
+ if ( $return ) {
+ print $return. ".";
+ }
+ else {
+ print "(Error: $msg)\n";
+ }
+ }
+ print "done.\n";
+ }
+ $RT::Handle->Disconnect() unless $RT::DatabaseType eq 'SQLite';
+ print "Done setting up database content.\n";
+}
-$0: Set up RT's database
+=head2 ACLEquivGroupId
---action init Initialize the database. This is combination of
- multiple actions listed below. Create DB, schema,
- setup acl, insert core data and initial data.
+Given a userid, return that user's acl equivalence group
- upgrade Apply all needed schema/acl/content updates (will ask
- for version to upgrade from)
+=cut
- create Create the database.
+sub ACLEquivGroupId {
+ my $username = shift;
+ my $user = RT::User->new($RT::SystemUser);
+ $user->Load($username);
+ my $equiv_group = RT::Group->new($RT::SystemUser);
+ $equiv_group->LoadACLEquivalenceGroup($user);
+ return ( $equiv_group->Id );
+}
- drop Drop the database.
- This will ERASE ALL YOUR DATA
+sub help {
- schema Initialize only the database schema
- To use a local or supplementary datafile, specify it
- using the '--datadir' option below.
+ print <<EOF;
- acl Initialize only the database ACLs
- To use a local or supplementary datafile, specify it
- using the '--datadir' option below.
+$0: Set up RT's database
- coredata Insert data into RT's database. This data is required
- for normal functioning of any RT instance.
+--action init Initialize the database
+ drop Drop the database.
+ This will ERASE ALL YOUR DATA
+ insert_initial
+ Insert RT's core system objects
+ insert Insert data into RT's database.
+ By default, will use RT's installation data.
+ To use a local or supplementary datafile, specify it
+ using the '--datafile' option below.
- insert Insert data into RT's database.
- By default, will use RT's installation data.
- To use a local or supplementary datafile, specify it
- using the '--datafile' option below.
+ acl Initialize only the database ACLs
+ To use a local or supplementary datafile, specify it
+ using the '--datadir' option below.
-Several actions can be combined using comma separated list.
+ schema Initialize only the database schema
+ To use a local or supplementary datafile, specify it
+ using the '--datadir' option below.
--datafile /path/to/datafile
--datadir /path/to/ Used to specify a path to find the local
- database schema and acls to be installed.
+ database schema and acls to be installed.
--dba dba's username
diff --git a/rt/sbin/rt-test-dependencies b/rt/sbin/rt-test-dependencies
deleted file mode 100644
index a1fed190b..000000000
--- a/rt/sbin/rt-test-dependencies
+++ /dev/null
@@ -1,574 +0,0 @@
-#!/usr/bin/perl
-# BEGIN BPS TAGGED BLOCK {{{
-#
-# COPYRIGHT:
-#
-# This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
-# <jesse@bestpractical.com>
-#
-# (Except where explicitly superseded by other copyright notices)
-#
-#
-# LICENSE:
-#
-# This work is made available to you under the terms of Version 2 of
-# the GNU General Public License. A copy of that license should have
-# been provided with this software, but in any event can be snarfed
-# from www.gnu.org.
-#
-# This work is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301 or visit their web page on the internet at
-# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-#
-#
-# CONTRIBUTION SUBMISSION POLICY:
-#
-# (The following paragraph is not intended to limit the rights granted
-# to you to modify and distribute this software under the terms of
-# the GNU General Public License and is only of importance to you if
-# you choose to contribute your changes and enhancements to the
-# community by submitting them to Best Practical Solutions, LLC.)
-#
-# By intentionally submitting any modifications, corrections or
-# derivatives to this work, or any other work intended for use with
-# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-# you are the copyright holder for those contributions and you grant
-# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
-# royalty-free, perpetual, license to use, copy, create derivative
-# works based on those contributions, and sublicense and distribute
-# those contributions and any derivatives thereof.
-#
-# END BPS TAGGED BLOCK }}}
-#
-# This is just a basic script that checks to make sure that all
-# the modules needed by RT before you can install it.
-#
-
-use strict;
-no warnings qw(numeric redefine);
-use Getopt::Long;
-my %args;
-my %deps;
-GetOptions(
- \%args, 'v|verbose',
- 'install', 'with-MYSQL',
- 'with-POSTGRESQL|with-pg|with-pgsql', 'with-SQLITE',
- 'with-ORACLE', 'with-FASTCGI',
- 'with-SPEEDYCGI', 'with-MODPERL1',
- 'with-MODPERL2', 'with-DEV',
- 'with-STANDALONE',
-
- 'with-GPG',
- 'with-ICAL',
- 'with-SMTP',
- 'with-GRAPHVIZ',
- 'with-GD',
- 'with-DASHBOARDS',
-
- 'download=s',
- 'repository=s',
- 'list-deps'
-);
-
-unless (keys %args) {
- help();
- exit(1);
-}
-
-# Set up defaults
-my %default = (
- 'with-MASON' => 1,
- 'with-CORE' => 1,
- 'with-CLI' => 1,
- 'with-MAILGATE' => 1,
- 'with-DEV' => 0,
- 'with-STANDALONE' => 1,
- 'with-GPG' => 1,
- 'with-ICAL' => 1,
- 'with-SMTP' => 1,
- 'with-GRAPHVIZ' => 0,
- 'with-GD' => 1,
- 'with-DASHBOARDS' => 1
-);
-$args{$_} = $default{$_} foreach grep !exists $args{$_}, keys %default;
-
-{
- my $section;
- my %always_show_sections = (
- perl => 1,
- users => 1,
- );
-
- sub section {
- my $s = shift;
- $section = $s;
- print "$s:\n" unless $args{'list-deps'};
- }
-
- sub print_found {
- my $msg = shift;
- my $test = shift;
- my $extra = shift;
-
- unless ( $args{'list-deps'} ) {
- if ( $args{'v'} or not $test or $always_show_sections{$section} ) {
- print "\t$msg ...";
- print $test ? "found" : "MISSING";
- print "\n";
- }
-
- print "\t\t$extra\n" if defined $extra;
- }
- }
-}
-
-sub conclude {
- my %missing_by_type = @_;
-
- unless ( $args{'list-deps'} ) {
- unless ( keys %missing_by_type ) {
- print "\nAll dependencies have been found.\n";
- return;
- }
-
- print "\nSOME DEPENDENCIES WERE MISSING.\n";
-
- for my $type ( keys %missing_by_type ) {
- my $missing = $missing_by_type{$type};
-
- print "$type missing dependencies:\n";
- for my $name ( keys %$missing ) {
- my $module = $missing->{$name};
- my $version = $module->{version};
- print_found( $name . ( $version ? " >= $version" : "" ),
- 0, $module->{error} );
- }
- }
- exit 1;
- }
-}
-
-
-sub help {
-
- print <<'.';
-
-By default, testdeps determine whether you have
-installed all the perl modules RT needs to run.
-
- --install Install missing modules
-
-The following switches will tell the tool to check for specific dependencies
-
- --with-mysql Database interface for MySQL
- --with-postgresql Database interface for PostgreSQL
- --with-oracle Database interface for Oracle
- --with-sqlite Database interface and driver for SQLite (unsupported)
-
- --with-standalone Libraries needed to support the standalone simple pure perl server
- --with-fastcgi Libraries needed to support the fastcgi handler
- --with-speedycgi Libraries needed to support the speedycgi handler
- --with-modperl1 Libraries needed to support the modperl 1 handler
- --with-modperl2 Libraries needed to support the modperl 2 handler
-
- --with-dev Tools needed for RT development
-
-You can also specify -v or --verbose to list the status of all dependencies,
-rather than just the missing ones.
-
-The "RT_FIX_DEPS_CMD" environment variable, if set, will be used
-instead of the standard CPAN shell by --install to install any
-required modules. It will be called with the module name, or, if
-"RT_FIX_DEPS_CMD" contains a "%s", will replace the "%s" with the
-module name before calling the program.
-.
-}
-
-
-sub text_to_hash {
- my %hash;
- for my $line ( split /\n/, $_[0] ) {
- my($key, $value) = $line =~ /(\S+)\s*(\S*)/;
- $value ||= '';
- $hash{$key} = $value;
- }
-
- return %hash;
-}
-
-$deps{'CORE'} = [ text_to_hash( << '.') ];
-Digest::base
-Digest::MD5 2.27
-DBI 1.37
-Class::ReturnValue 0.40
-DBIx::SearchBuilder 1.54
-Text::Template 1.44
-File::ShareDir
-File::Spec 0.8
-HTML::Entities
-HTML::Scrubber 0.08
-Log::Dispatch 2.0
-Sys::Syslog 0.16
-Locale::Maketext 1.06
-Locale::Maketext::Lexicon 0.32
-Locale::Maketext::Fuzzy
-MIME::Entity 5.425
-Mail::Mailer 1.57
-Email::Address
-Text::Wrapper
-Time::ParseDate
-Time::HiRes
-File::Temp 0.18
-Text::Quoted 2.02
-Tree::Simple 1.04
-UNIVERSAL::require
-Regexp::Common
-Scalar::Util
-Module::Versions::Report 1.05
-Cache::Simple::TimedExpiry
-Calendar::Simple
-Encode 2.21
-CSS::Squish 0.06
-File::Glob
-Devel::StackTrace 1.19
-.
-
-$deps{'MASON'} = [ text_to_hash( << '.') ];
-HTML::Mason 1.36
-Errno
-Digest::MD5 2.27
-CGI::Cookie 1.20
-Storable 2.08
-Apache::Session 1.53
-XML::RSS 1.05
-Text::WikiFormat 0.76
-CSS::Squish 0.06
-Devel::StackTrace 1.19
-.
-
-$deps{'STANDALONE'} = [ text_to_hash( << '.') ];
-HTTP::Server::Simple 0.34
-HTTP::Server::Simple::Mason 0.09
-Net::Server
-.
-
-$deps{'MAILGATE'} = [ text_to_hash( << '.') ];
-HTML::TreeBuilder
-HTML::FormatText
-Getopt::Long
-LWP::UserAgent
-Pod::Usage
-.
-
-$deps{'CLI'} = [ text_to_hash( << '.') ];
-Getopt::Long 2.24
-LWP
-HTTP::Request::Common
-Text::ParseWords
-Term::ReadLine
-Term::ReadKey
-.
-
-$deps{'DEV'} = [ text_to_hash( << '.') ];
-HTML::Form
-HTML::TokeParser
-WWW::Mechanize
-Test::WWW::Mechanize 1.04
-Module::Refresh 0.03
-Test::Expect 0.31
-XML::Simple
-File::Find
-Test::Deep 0 # needed for shredder tests
-String::ShellQuote 0 # needed for gnupg-incoming.t
-Test::HTTP::Server::Simple 0.09
-Test::HTTP::Server::Simple::StashWarnings 0.02
-Log::Dispatch::Perl
-Test::Warn
-Test::Builder 0.77 # needed to fix TODO test
-IPC::Run3
-Test::MockTime
-HTTP::Server::Simple::Mason 0.13
-.
-
-$deps{'FASTCGI'} = [ text_to_hash( << '.') ];
-CGI 3.38
-FCGI
-CGI::Fast
-.
-
-$deps{'SPEEDYCGI'} = [ text_to_hash( << '.') ];
-CGI 3.38
-CGI::SpeedyCGI
-.
-
-
-$deps{'MODPERL1'} = [ text_to_hash( << '.') ];
-CGI 3.38
-Apache::Request
-Apache::DBI 0.92
-.
-
-$deps{'MODPERL2'} = [ text_to_hash( << '.') ];
-CGI 3.38
-Apache::DBI
-HTML::Mason 1.36
-.
-
-$deps{'MYSQL'} = [ text_to_hash( << '.') ];
-DBD::mysql 2.1018
-.
-
-$deps{'ORACLE'} = [ text_to_hash( << '.') ];
-DBD::Oracle
-.
-
-$deps{'POSTGRESQL'} = [ text_to_hash( << '.') ];
-DBD::Pg 1.43
-.
-
-$deps{'SQLITE'} = [ text_to_hash( << '.') ];
-DBD::SQLite 1.00
-.
-
-$deps{'GPG'} = [ text_to_hash( << '.') ];
-GnuPG::Interface
-PerlIO::eol
-.
-
-$deps{'ICAL'} = [ text_to_hash( << '.') ];
-Data::ICal
-.
-
-$deps{'SMTP'} = [ text_to_hash( << '.') ];
-Net::SMTP
-.
-
-$deps{'DASHBOARDS'} = [ text_to_hash( << '.') ];
-HTML::RewriteAttributes 0.02
-MIME::Types
-.
-
-$deps{'GRAPHVIZ'} = [ text_to_hash( << '.') ];
-GraphViz
-IPC::Run
-IPC::Run::SafeHandles
-.
-
-$deps{'GD'} = [ text_to_hash( << '.') ];
-GD
-GD::Graph
-GD::Text
-.
-
-if ($args{'download'}) {
-
- download_mods();
-}
-
-
-check_perl_version();
-
-check_users();
-
-my %Missing_By_Type = ();
-foreach my $type (sort grep $args{$_}, keys %args) {
- next unless ($type =~ /^with-(.*?)$/);
-
- $type = $1;
- section("$type dependencies");
-
- my @missing;
- my @deps = @{ $deps{$type} };
-
- my %missing = test_deps(@deps);
-
- if ( $args{'install'} ) {
- for my $module (keys %missing) {
- resolve_dep($module, $missing{$module}{version});
- delete $missing{$module} if test_dep($module, $missing{$module}{version});
- }
- }
-
- $Missing_By_Type{$type} = \%missing if keys %missing;
-}
-
-conclude(%Missing_By_Type);
-
-sub test_deps {
- my @deps = @_;
-
- my %missing;
- while(@deps) {
- my $module = shift @deps;
- my $version = shift @deps;
- my($test, $error) = test_dep($module, $version);
- my $msg = $module . ($version ? " >= $version" : '');
- print_found($msg, $test, $error);
-
- $missing{$module} = { version => $version, error => $error } unless $test;
- }
-
- return %missing;
-}
-
-sub test_dep {
- my $module = shift;
- my $version = shift;
-
- if ( $args{'list-deps'} ) {
- print $module, ': ', $version || 0, "\n";
- }
- else {
- eval "use $module $version ()";
- if ($@) {
- my $error = $@;
- $error =~ s/\n(.*)$//s;
- $error =~ s/at \(eval \d+\) line \d+\.$//;
- undef $error unless $error =~ /this is only/;
-
- return ( 0, $error );
- }
- else {
- return 1;
- }
- }
-}
-
-sub resolve_dep {
- my $module = shift;
- my $version = shift;
-
- print "\nInstall module $module\n";
-
- my $ext = $ENV{'RT_FIX_DEPS_CMD'};
- unless( $ext ) {
- my $configured = 1;
- {
- local @INC = @INC;
- if ( $ENV{'HOME'} ) {
- unshift @INC, "$ENV{'HOME'}/.cpan";
- }
- $configured = eval { require CPAN::MyConfig } || eval { require CPAN::Config };
- }
- unless ( $configured ) {
- print <<END;
-You haven't configured the CPAN shell yet.
-Please run `/usr/bin/perl -MCPAN -e shell` to configure it.
-END
- exit(1);
- }
- my $rv = eval { require CPAN; CPAN::Shell->install($module) };
- return $rv unless $@;
-
- print <<END;
-Failed to load module CPAN.
-
--------- Error ---------
-$@
-------------------------
-
-When we tried to start installing RT's perl dependencies,
-we were unable to load the CPAN client. This module is usually distributed
-with Perl. This usually indicates that your vendor has shipped an unconfigured
-or incorrectly configured CPAN client.
-The error above may (or may not) give you a hint about what went wrong
-
-You have several choices about how to install dependencies in
-this situatation:
-
-1) use a different tool to install dependencies by running setting the following
- shell environment variable and rerunning this tool:
- RT_FIX_DEPS_CMD='/usr/bin/perl -MCPAN -e"install %s"'
-2) Attempt to configure CPAN by running:
- `/usr/bin/perl -MCPAN -e shell` program from shell.
- If this fails, you may have to manually upgrade CPAN (see below)
-3) Try to update the CPAN client. Download it from:
- http://search.cpan.org/dist/CPAN and try again
-4) Install each dependency manually by downloading them one by one from
- http://search.cpan.org
-
-END
- exit(1);
- }
-
- if( $ext =~ /\%s/) {
- $ext =~ s/\%s/$module/g; # sprintf( $ext, $module );
- } else {
- $ext .= " $module";
- }
- print "\t\tcommand: '$ext'\n";
- return scalar `$ext 1>&2`;
-}
-
-sub download_mods {
- my %modules;
- use CPAN;
-
- foreach my $key (keys %deps) {
- my @deps = (@{$deps{$key}});
- while (@deps) {
- my $mod = shift @deps;
- my $ver = shift @deps;
- next if ($mod =~ /^(DBD-|Apache-Request)/);
- $modules{$mod} = $ver;
- }
- }
- my @mods = keys %modules;
- CPAN::get();
- my $moddir = $args{'download'};
- foreach my $mod (@mods) {
- $CPAN::Config->{'build_dir'} = $moddir;
- CPAN::get($mod);
- }
-
- opendir(DIR, $moddir);
- while ( my $dir = readdir(DIR)) {
- print "Dir is $dir\n";
- next if ( $dir =~ /^\.\.?$/);
-
- # Skip things we've previously tagged
- my $out = `svn ls $args{'repository'}/tags/$dir`;
- next if ($out);
-
- if ($dir =~ /^(.*)-(.*?)$/) {
- `svn_load_dirs -no_user_input -t tags/$dir -v $args{'repository'} dists/$1 $moddir/$dir`;
- `rm -rf $moddir/$dir`;
-
- }
-
- }
- closedir(DIR);
- exit;
-}
-
-sub check_perl_version {
- section("perl");
- eval {require 5.008003};
- if ($@) {
- print_found("5.8.3", 0,"RT is known to be non-functional on versions of perl older than 5.8.3. Please upgrade to 5.8.3 or newer.");
- exit(1);
- } else {
- print_found( sprintf(">=5.8.3(%vd)", $^V), 1 );
- }
-}
-
-sub check_users {
- section("users");
- print_found("rt group (www)", defined getgrnam("www"));
- print_found("bin owner (root)", defined getpwnam("root"));
- print_found("libs owner (root)", defined getpwnam("root"));
- print_found("libs group (bin)", defined getgrnam("bin"));
- print_found("web owner (www)", defined getpwnam("www"));
- print_found("web group (www)", defined getgrnam("www"));
-}
-
-
-
-1;