From 6587f6ba7d047ddc1686c080090afe7d53365bd4 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Tue, 24 Apr 2012 11:35:56 -0700 Subject: [PATCH] first pass RT4 merge, RT#13852 --- rt/bin/rt | 2616 ++++ rt/devel/tools/apache.conf | 173 + rt/devel/tools/change-loc-msgstr | 92 + rt/devel/tools/extract-message-catalog | 385 + rt/devel/tools/factory | 372 + rt/devel/tools/license_tag | 262 + rt/devel/tools/merge-rosetta.pl | 49 + rt/devel/tools/rt-attributes-editor | 130 + rt/devel/tools/tweak-template-locstring | 55 + rt/docs/UPGRADING-2.0 | 7 + rt/docs/UPGRADING-3.0 | 18 + rt/docs/UPGRADING-3.2 | 11 + rt/docs/UPGRADING-3.4 | 12 + rt/docs/UPGRADING-3.6 | 49 + rt/docs/UPGRADING-3.8 | 192 + rt/docs/UPGRADING-4.0 | 108 + rt/docs/UPGRADING.mysql | 85 + rt/docs/customizing/articles_introduction.pod | 155 + rt/docs/customizing/templates.pod | 132 + rt/docs/customizing/timezones_in_charts.pod | 88 + rt/docs/extending/clickable_links.pod | 184 + rt/docs/extending/external_custom_fields.pod | 90 + rt/docs/extending/using_forms_widgets.pod | 113 + rt/docs/full_text_indexing.pod | 174 + rt/docs/glossary.pod | 30 + rt/docs/hacking.pod | 301 + rt/docs/network-diagram.svg | 6095 +++++++++ rt/docs/schema.dot | 99 + rt/docs/security.pod | 77 + rt/docs/web_deployment.pod | 233 + rt/etc/schema.mysql | 499 + rt/etc/upgrade/3.9.1/content | 68 + rt/etc/upgrade/3.9.2/content | 48 + rt/etc/upgrade/3.9.3/schema.Oracle | 2 + rt/etc/upgrade/3.9.3/schema.Pg | 2 + rt/etc/upgrade/3.9.3/schema.mysql | 2 + rt/etc/upgrade/3.9.5/backcompat | 1 + rt/etc/upgrade/3.9.5/schema.Oracle | 20 + rt/etc/upgrade/3.9.5/schema.Pg | 20 + rt/etc/upgrade/3.9.5/schema.SQLite | 19 + rt/etc/upgrade/3.9.5/schema.mysql | 20 + rt/etc/upgrade/3.9.6/schema.Oracle | 1 + rt/etc/upgrade/3.9.6/schema.Pg | 1 + rt/etc/upgrade/3.9.6/schema.SQLite | 69 + rt/etc/upgrade/3.9.6/schema.mysql | 1 + rt/etc/upgrade/3.9.7/content | 82 + rt/etc/upgrade/3.9.7/schema.Oracle | 6 + rt/etc/upgrade/3.9.7/schema.Pg | 6 + rt/etc/upgrade/3.9.7/schema.SQLite | 6 + rt/etc/upgrade/3.9.7/schema.mysql | 6 + rt/etc/upgrade/3.9.8/content | 24 + rt/etc/upgrade/3.9.8/schema.Oracle | 65 + rt/etc/upgrade/3.9.8/schema.Pg | 62 + rt/etc/upgrade/3.9.8/schema.SQLite | 55 + rt/etc/upgrade/3.9.8/schema.mysql | 58 + rt/etc/upgrade/4.0.0rc2/schema.mysql | 10 + rt/etc/upgrade/4.0.0rc4/schema.Oracle | 1 + rt/etc/upgrade/4.0.0rc4/schema.Pg | 1 + rt/etc/upgrade/4.0.0rc4/schema.mysql | 1 + rt/etc/upgrade/4.0.0rc7/content | 21 + rt/etc/upgrade/4.0.1/acl.Pg | 39 + rt/etc/upgrade/4.0.1/content | 83 + rt/etc/upgrade/4.0.3/content | 23 + rt/etc/upgrade/4.0.4/content | 16 + rt/etc/upgrade/sanity-check-stylesheets.pl | 87 + rt/etc/upgrade/upgrade-articles | 264 + rt/etc/upgrade/upgrade-articles.in | 264 + rt/lib/RT/Action/SetStatus.pm | 152 + rt/lib/RT/Article.pm | 870 ++ rt/lib/RT/Articles.pm | 925 ++ rt/lib/RT/Class.pm | 620 + rt/lib/RT/Classes.pm | 104 + rt/lib/RT/Dashboard/Mailer.pm | 577 + rt/lib/RT/Dashboards.pm | 112 + rt/lib/RT/Generated.pm | 81 + rt/lib/RT/Generated.pm.in | 81 + rt/lib/RT/Lifecycle.pm | 670 + rt/lib/RT/ObjectClass.pm | 222 + rt/lib/RT/ObjectClasses.pm | 87 + rt/lib/RT/ObjectTopic.pm | 214 + rt/lib/RT/ObjectTopics.pm | 115 + rt/lib/RT/SharedSettings.pm | 155 + rt/lib/RT/Squish.pm | 122 + rt/lib/RT/Squish/CSS.pm | 105 + rt/lib/RT/Squish/JS.pm | 127 + rt/lib/RT/Test/Apache.pm | 270 + rt/lib/RT/Test/GnuPG.pm | 360 + rt/lib/RT/Tickets_SQL.pm | 423 + rt/lib/RT/Topic.pm | 376 + rt/lib/RT/Topics.pm | 119 + rt/lib/RT/URI/a.pm | 92 + rt/lib/RT/URI/fsck_com_article.pm | 216 + rt/sbin/rt-dump-metadata | 251 + rt/sbin/rt-dump-metadata.in | 251 + rt/sbin/rt-fulltext-indexer | 453 + rt/sbin/rt-fulltext-indexer.in | 453 + rt/sbin/rt-preferences-viewer | 149 + rt/sbin/rt-preferences-viewer.in | 149 + rt/sbin/rt-server.fcgi | 282 + rt/sbin/rt-server.fcgi.in | 282 + rt/sbin/rt-setup-database | 590 + rt/sbin/rt-setup-fulltext-index | 714 + rt/sbin/rt-setup-fulltext-index.in | 714 + rt/sbin/rt-test-dependencies | 661 + rt/sbin/standalone_httpd | 282 + rt/sbin/standalone_httpd.in | 282 + .../html/Admin/Articles/Classes/CustomFields.html | 64 + .../html/Admin/Articles/Classes/GroupRights.html | 73 + rt/share/html/Admin/Articles/Classes/Modify.html | 199 + rt/share/html/Admin/Articles/Classes/Objects.html | 154 + rt/share/html/Admin/Articles/Classes/Topics.html | 68 + .../html/Admin/Articles/Classes/UserRights.html | 72 + rt/share/html/Admin/Articles/Classes/index.html | 91 + rt/share/html/Admin/Articles/Elements/Topics | 215 + rt/share/html/Admin/Articles/index.html | 50 + rt/share/html/Admin/Elements/EditQueueWatcherGroup | 56 + rt/share/html/Admin/Elements/EditRights | 175 + .../html/Admin/Elements/EditRightsCategoryTabs | 129 + rt/share/html/Admin/Elements/Portal | 52 + .../Admin/Elements/SelectCustomFieldRenderType | 70 + .../Admin/Global/CustomFields/Class-Article.html | 54 + rt/share/html/Admin/Global/Topics.html | 63 + rt/share/html/Admin/Tools/Queries.html | 129 + rt/share/html/Admin/Tools/Theme.html | 309 + rt/share/html/Articles/Article/Delete.html | 106 + rt/share/html/Articles/Article/Display.html | 92 + rt/share/html/Articles/Article/Edit.html | 333 + rt/share/html/Articles/Article/Elements/EditBasics | 73 + .../Articles/Article/Elements/EditCustomFields | 83 + rt/share/html/Articles/Article/Elements/EditLinks | 113 + rt/share/html/Articles/Article/Elements/EditTopics | 147 + .../Article/Elements/LinkEntryInstructions | 49 + .../html/Articles/Article/Elements/Preformatted | 127 + .../Articles/Article/Elements/SearchByCustomField | 70 + .../Articles/Article/Elements/SelectSavedSearches | 76 + .../Articles/Article/Elements/SelectSearchPrivacy | 62 + .../html/Articles/Article/Elements/ShowHistory | 76 + rt/share/html/Articles/Article/Elements/ShowLinks | 92 + .../Articles/Article/Elements/ShowSavedSearches | 85 + .../Articles/Article/Elements/ShowSearchCriteria | 171 + rt/share/html/Articles/Article/Elements/ShowTopics | 58 + .../html/Articles/Article/ExtractFromTicket.html | 105 + .../html/Articles/Article/ExtractIntoClass.html | 76 + .../html/Articles/Article/ExtractIntoTopic.html | 72 + rt/share/html/Articles/Article/History.html | 57 + rt/share/html/Articles/Article/PreCreate.html | 62 + rt/share/html/Articles/Article/Search.html | 269 + rt/share/html/Articles/Elements/BeforeMessageBox | 239 + rt/share/html/Articles/Elements/CheckSkipCreate | 76 + rt/share/html/Articles/Elements/CreateArticle | 59 + rt/share/html/Articles/Elements/GotoArticle | 73 + rt/share/html/Articles/Elements/IncludeArticle | 99 + rt/share/html/Articles/Elements/NewestArticles | 80 + rt/share/html/Articles/Elements/QuickSearch | 59 + rt/share/html/Articles/Elements/SelectClass | 95 + rt/share/html/Articles/Elements/ShowTopic | 64 + rt/share/html/Articles/Elements/UpdatedArticles | 81 + rt/share/html/Articles/Topics.html | 276 + rt/share/html/Articles/index.html | 63 + rt/share/html/Elements/EditCustomFieldDateTime | 62 + rt/share/html/Elements/EditCustomFieldIPAddress | 50 + .../html/Elements/EditCustomFieldIPAddressRange | 50 + rt/share/html/Elements/Framekiller | 67 + rt/share/html/Elements/QueriesAsComment | 58 + rt/share/html/Elements/QueueSummaryByLifecycle | 145 + rt/share/html/Elements/QueueSummaryByStatus | 139 + rt/share/html/Elements/RT__Article/ColumnMap | 107 + rt/share/html/Elements/RT__Class/ColumnMap | 76 + rt/share/html/Elements/RT__Dashboard/ColumnMap | 134 + rt/share/html/Elements/SelectIPRelation | 64 + rt/share/html/Elements/SelectOwnerAutocomplete | 102 + rt/share/html/Elements/SelectOwnerDropdown | 99 + rt/share/html/Elements/ShowCustomFieldDateTime | 57 + rt/share/html/Elements/ShowReminders | 101 + rt/share/html/Elements/WidgetBar | 58 + rt/share/html/Helpers/Autocomplete/Groups | 88 + rt/share/html/Helpers/Autocomplete/Owners | 150 + rt/share/html/Helpers/Autocomplete/Users | 130 + rt/share/html/Helpers/TicketHistory | 67 + rt/share/html/Helpers/Toggle/ShowRequestor | 63 + rt/share/html/NoAuth/Helpers/CustomLogo/dhandler | 61 + .../html/NoAuth/RichText/ckeditor/LICENSE.html | 1334 ++ .../NoAuth/RichText/ckeditor/adapters/jquery.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/ckeditor.js | 135 + rt/share/html/NoAuth/RichText/ckeditor/config.js | 47 + .../html/NoAuth/RichText/ckeditor/contents.css | 35 + .../NoAuth/RichText/ckeditor/lang/_languages.js | 6 + .../RichText/ckeditor/lang/_translationstatus.txt | 60 + rt/share/html/NoAuth/RichText/ckeditor/lang/af.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/ar.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/bg.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/bn.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/bs.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/ca.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/cs.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/cy.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/da.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/de.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/el.js | 6 + .../html/NoAuth/RichText/ckeditor/lang/en-au.js | 6 + .../html/NoAuth/RichText/ckeditor/lang/en-ca.js | 6 + .../html/NoAuth/RichText/ckeditor/lang/en-gb.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/en.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/eo.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/es.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/et.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/eu.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/fa.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/fi.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/fo.js | 6 + .../html/NoAuth/RichText/ckeditor/lang/fr-ca.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/fr.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/gl.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/gu.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/he.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/hi.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/hr.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/hu.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/is.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/it.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/ja.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/km.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/ko.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/lt.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/lv.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/mn.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/ms.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/nb.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/nl.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/no.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/pl.js | 6 + .../html/NoAuth/RichText/ckeditor/lang/pt-br.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/pt.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/ro.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/ru.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/sk.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/sl.js | 6 + .../html/NoAuth/RichText/ckeditor/lang/sr-latn.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/sr.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/sv.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/th.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/tr.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/uk.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/vi.js | 6 + .../html/NoAuth/RichText/ckeditor/lang/zh-cn.js | 6 + rt/share/html/NoAuth/RichText/ckeditor/lang/zh.js | 6 + .../ckeditor/plugins/a11yhelp/dialogs/a11yhelp.js | 7 + .../RichText/ckeditor/plugins/a11yhelp/lang/en.js | 6 + .../RichText/ckeditor/plugins/a11yhelp/lang/he.js | 6 + .../ckeditor/plugins/about/dialogs/about.js | 6 + .../RichText/ckeditor/plugins/autogrow/plugin.js | 6 + .../ckeditor/plugins/clipboard/dialogs/paste.js | 7 + .../plugins/colordialog/dialogs/colordialog.js | 7 + .../ckeditor/plugins/dialog/dialogDefinition.js | 4 + .../RichText/ckeditor/plugins/div/dialogs/div.js | 8 + .../RichText/ckeditor/plugins/find/dialogs/find.js | 9 + .../ckeditor/plugins/flash/dialogs/flash.js | 9 + .../ckeditor/plugins/forms/dialogs/button.js | 6 + .../ckeditor/plugins/forms/dialogs/checkbox.js | 6 + .../ckeditor/plugins/forms/dialogs/form.js | 6 + .../ckeditor/plugins/forms/dialogs/hiddenfield.js | 6 + .../ckeditor/plugins/forms/dialogs/radio.js | 6 + .../ckeditor/plugins/forms/dialogs/select.js | 9 + .../ckeditor/plugins/forms/dialogs/textarea.js | 6 + .../ckeditor/plugins/forms/dialogs/textfield.js | 7 + .../ckeditor/plugins/iframedialog/plugin.js | 6 + .../ckeditor/plugins/image/dialogs/image.js | 13 + .../ckeditor/plugins/link/dialogs/anchor.js | 6 + .../RichText/ckeditor/plugins/link/dialogs/link.js | 11 + .../plugins/liststyle/dialogs/liststyle.js | 7 + .../plugins/pastefromword/filter/default.js | 10 + .../plugins/pastetext/dialogs/pastetext.js | 6 + .../ckeditor/plugins/scayt/dialogs/options.js | 8 + .../ckeditor/plugins/scayt/dialogs/toolbar.css | 6 + .../ckeditor/plugins/smiley/dialogs/smiley.js | 7 + .../plugins/specialchar/dialogs/specialchar.js | 7 + .../ckeditor/plugins/styles/styles/default.js | 6 + .../ckeditor/plugins/table/dialogs/table.js | 9 + .../ckeditor/plugins/tableresize/plugin.js | 7 + .../plugins/tabletools/dialogs/tableCell.js | 8 + .../plugins/templates/dialogs/templates.js | 7 + .../plugins/templates/templates/default.js | 6 + .../ckeditor/plugins/uicolor/dialogs/uicolor.js | 7 + .../RichText/ckeditor/plugins/uicolor/lang/en.js | 6 + .../RichText/ckeditor/plugins/uicolor/plugin.js | 6 + .../ckeditor/plugins/uicolor/yui/assets/yui.css | 6 + .../RichText/ckeditor/plugins/uicolor/yui/yui.js | 76 + .../ckeditor/plugins/wsc/dialogs/ciframe.html | 49 + .../ckeditor/plugins/wsc/dialogs/tmpFrameset.html | 52 + .../RichText/ckeditor/plugins/wsc/dialogs/wsc.css | 6 + .../RichText/ckeditor/plugins/wsc/dialogs/wsc.js | 7 + .../NoAuth/RichText/ckeditor/skins/kama/dialog.css | 9 + .../NoAuth/RichText/ckeditor/skins/kama/editor.css | 12 + .../NoAuth/RichText/ckeditor/skins/kama/skin.js | 7 + .../RichText/ckeditor/skins/kama/templates.css | 6 + .../RichText/ckeditor/skins/office2003/dialog.css | 9 + .../RichText/ckeditor/skins/office2003/editor.css | 13 + .../RichText/ckeditor/skins/office2003/skin.js | 6 + .../ckeditor/skins/office2003/templates.css | 6 + .../NoAuth/RichText/ckeditor/skins/v2/dialog.css | 8 + .../NoAuth/RichText/ckeditor/skins/v2/editor.css | 12 + .../html/NoAuth/RichText/ckeditor/skins/v2/skin.js | 6 + .../RichText/ckeditor/skins/v2/templates.css | 6 + .../RichText/ckeditor/themes/default/theme.js | 8 + rt/share/html/NoAuth/css/aileron/InHeader | 67 + rt/share/html/NoAuth/css/aileron/base.css | 62 + rt/share/html/NoAuth/css/aileron/boxes.css | 180 + rt/share/html/NoAuth/css/aileron/forms.css | 79 + rt/share/html/NoAuth/css/aileron/images/dhandler | 8 + rt/share/html/NoAuth/css/aileron/layout.css | 174 + rt/share/html/NoAuth/css/aileron/login.css | 50 + rt/share/html/NoAuth/css/aileron/main.css | 63 + rt/share/html/NoAuth/css/aileron/misc.css | 58 + rt/share/html/NoAuth/css/aileron/msie-pie.css | 58 + rt/share/html/NoAuth/css/aileron/msie.css | 188 + rt/share/html/NoAuth/css/aileron/msie6.css | 86 + rt/share/html/NoAuth/css/aileron/nav.css | 164 + rt/share/html/NoAuth/css/aileron/ticket-lists.css | 236 + rt/share/html/NoAuth/css/aileron/ticket-search.css | 242 + rt/share/html/NoAuth/css/aileron/ticket.css | 289 + rt/share/html/NoAuth/css/ballard/InHeader | 54 + rt/share/html/NoAuth/css/ballard/base.css | 62 + rt/share/html/NoAuth/css/ballard/boxes.css | 184 + rt/share/html/NoAuth/css/ballard/images/dhandler | 8 + rt/share/html/NoAuth/css/ballard/layout.css | 173 + rt/share/html/NoAuth/css/ballard/main.css | 62 + rt/share/html/NoAuth/css/ballard/misc.css | 58 + rt/share/html/NoAuth/css/ballard/msie.css | 242 + rt/share/html/NoAuth/css/ballard/msie6.css | 88 + rt/share/html/NoAuth/css/ballard/nav.css | 189 + rt/share/html/NoAuth/css/ballard/ticket-lists.css | 236 + rt/share/html/NoAuth/css/ballard/ticket-search.css | 243 + rt/share/html/NoAuth/css/ballard/ticket.css | 275 + rt/share/html/NoAuth/css/base/admin.css | 97 + rt/share/html/NoAuth/css/base/articles.css | 54 + rt/share/html/NoAuth/css/base/collection.css | 50 + rt/share/html/NoAuth/css/base/farbtastic.css | 98 + rt/share/html/NoAuth/css/base/forms.css | 270 + rt/share/html/NoAuth/css/base/history-folding.css | 91 + rt/share/html/NoAuth/css/base/jquery-ui.css | 50 + .../NoAuth/css/base/jquery-ui.custom.modified.css | 454 + rt/share/html/NoAuth/css/base/login.css | 102 + rt/share/html/NoAuth/css/base/main.css | 73 + rt/share/html/NoAuth/css/base/misc.css | 60 +- rt/share/html/NoAuth/css/base/nav.css | 64 + rt/share/html/NoAuth/css/base/portlets.css | 64 + rt/share/html/NoAuth/css/base/rights-editor.css | 177 + rt/share/html/NoAuth/css/base/superfish-navbar.css | 93 + .../html/NoAuth/css/base/superfish-vertical.css | 23 + rt/share/html/NoAuth/css/base/superfish.css | 136 + rt/share/html/NoAuth/css/base/tablesorter.css | 52 + rt/share/html/NoAuth/css/base/theme-editor.css | 111 + rt/share/html/NoAuth/css/base/ticket-form.css | 129 + rt/share/html/NoAuth/css/base/ticket.css | 146 + rt/share/html/NoAuth/css/base/tools.css | 54 + rt/share/html/NoAuth/css/base/ui.timepickr.css | 56 + .../html/NoAuth/css/base/ui.timepickr.custom.css | 54 + rt/share/html/NoAuth/css/base/yui-fonts.css | 7 + rt/share/html/NoAuth/css/images/PIE.htc | 77 + rt/share/html/NoAuth/css/web2/msie-pie.css | 60 + rt/share/html/NoAuth/js/autohandler | 2 +- rt/share/html/NoAuth/js/cascaded.js | 4 +- rt/share/html/NoAuth/js/combobox.js | 2 +- rt/share/html/NoAuth/js/dhandler | 65 + rt/share/html/NoAuth/js/farbtastic.js | 347 + rt/share/html/NoAuth/js/history-folding.js | 73 + rt/share/html/NoAuth/js/jquery-1.4.2.min.js | 154 + .../html/NoAuth/js/jquery-ui-1.8.4.custom.min.js | 224 + .../html/NoAuth/js/jquery-ui-patch-datepicker.js | 61 + rt/share/html/NoAuth/js/jquery.event.hover-1.0.js | 85 + rt/share/html/NoAuth/js/jquery.supposition.js | 83 + rt/share/html/NoAuth/js/jquery.tablesorter.min.js | 16 + rt/share/html/NoAuth/js/jquery_noconflict.js | 51 + rt/share/html/NoAuth/js/late.js | 49 + rt/share/html/NoAuth/js/superfish.js | 121 + rt/share/html/NoAuth/js/supersubs.js | 90 + rt/share/html/NoAuth/js/titlebox-state.js | 2 +- rt/share/html/NoAuth/js/ui.timepickr.js | 941 ++ rt/share/html/NoAuth/js/userautocomplete.js | 110 + rt/share/html/NoAuth/js/util.js | 393 +- rt/share/html/Search/Article.html | 52 + rt/share/html/Search/Elements/Article | 64 + rt/share/html/Search/Elements/EditSort | 142 + rt/share/html/SelfService/Article/Display.html | 73 + rt/share/html/SelfService/Article/Search.html | 114 + rt/share/html/SelfService/Article/autohandler | 58 + rt/share/html/SelfService/Elements/SearchArticle | 51 + rt/share/html/Ticket/Elements/AddAttachments | 61 + rt/share/html/Ticket/Elements/ClickToShowHistory | 60 + rt/share/html/Ticket/Elements/FindTransactions | 72 + rt/share/html/Ticket/Elements/FoldStanzaJS | 50 + .../html/Ticket/Elements/ShowRequestorExtraInfo | 89 + rt/share/html/Ticket/Elements/ShowRequestorTickets | 79 + .../Ticket/Elements/ShowRequestorTicketsActive | 60 + .../html/Ticket/Elements/ShowRequestorTicketsAll | 53 + .../Ticket/Elements/ShowRequestorTicketsInactive | 60 + .../html/Ticket/Elements/ShowSimplifiedRecipients | 100 + rt/share/html/Ticket/autohandler | 20 + rt/share/html/Tools/MyReminders.html | 55 + rt/share/html/m/ticket/autohandler | 8 + rt/share/po/bg.po | 9473 ++++++++++++++ rt/share/po/cs.po | 9541 ++++++++++++++ rt/share/po/da.po | 11032 ++++++++++++++++ rt/share/po/de.po | 10152 +++++++++++++++ rt/share/po/el.po | 9449 ++++++++++++++ rt/share/po/en.po | 23 + rt/share/po/en_GB.po | 7654 +++++++++++ rt/share/po/es.po | 11165 ++++++++++++++++ rt/share/po/et.po | 9225 +++++++++++++ rt/share/po/fi.po | 10534 +++++++++++++++ rt/share/po/fr.po | 9949 ++++++++++++++ rt/share/po/he.po | 9241 +++++++++++++ rt/share/po/hr.po | 10514 +++++++++++++++ rt/share/po/hu.po | 9443 ++++++++++++++ rt/share/po/id.po | 9870 ++++++++++++++ rt/share/po/is.po | 8873 +++++++++++++ rt/share/po/it.po | 11177 ++++++++++++++++ rt/share/po/ja.po | 9295 +++++++++++++ rt/share/po/lt.po | 9665 ++++++++++++++ rt/share/po/lv.po | 9446 ++++++++++++++ rt/share/po/mk.po | 8721 +++++++++++++ rt/share/po/nb.po | 11105 ++++++++++++++++ rt/share/po/nl.po | 10517 +++++++++++++++ rt/share/po/nn.po | 9562 ++++++++++++++ rt/share/po/pl.po | 10816 +++++++++++++++ rt/share/po/pt.po | 9633 ++++++++++++++ rt/share/po/pt_BR.po | 11233 ++++++++++++++++ rt/share/po/pt_PT.po | 9053 +++++++++++++ rt/share/po/rt.pot | 8696 ++++++++++++ rt/share/po/ru.po | 9611 ++++++++++++++ rt/share/po/sl.po | 9438 ++++++++++++++ rt/share/po/sv.po | 9792 ++++++++++++++ rt/share/po/tr.po | 9634 ++++++++++++++ rt/share/po/zh_CN.po | 13016 ++++++++++++++++++ rt/share/po/zh_TW.po | 13049 +++++++++++++++++++ rt/t/api/action-createtickets.t | 44 +- rt/t/api/attachment.t | 2 - rt/t/api/attachment_filename.t | 26 +- rt/t/api/attribute-tests.t | 7 +- rt/t/api/attribute.t | 11 +- rt/t/api/bookmarks.t | 24 + rt/t/api/canonical_charset.t | 30 + rt/t/api/cf_render_type.t | 49 + rt/t/api/condition-ownerchange.t | 7 +- rt/t/api/condition-reject.t | 7 +- rt/t/api/config.t | 33 + rt/t/api/cron.t | 89 + rt/t/api/currentuser.t | 3 +- rt/t/api/customfield.t | 11 +- rt/t/api/date.t | 98 +- rt/t/api/emailparser.t | 23 +- rt/t/api/execute-code.t | 108 + rt/t/api/group-rights.t | 137 + rt/t/api/group.t | 19 +- rt/t/api/groups.t | 111 +- rt/t/api/has_rights.t | 43 + rt/t/api/i18n.t | 3 +- rt/t/api/i18n_guess.t | 71 + rt/t/api/link.t | 61 +- rt/t/api/password-types.t | 20 +- rt/t/api/queue.t | 21 +- rt/t/api/record.t | 23 +- rt/t/api/reminders.t | 10 +- rt/t/api/rights.t | 18 +- rt/t/api/rights_show_ticket.t | 28 +- rt/t/api/rt.t | 11 +- rt/t/api/rtname.t | 34 + rt/t/api/safe-run-child-util.t | 201 + rt/t/api/savedsearch.t | 181 + rt/t/api/scrip.t | 10 +- rt/t/api/scrip_order.t | 11 +- rt/t/api/searchbuilder.t | 3 +- rt/t/api/squish.t | 16 + rt/t/api/system.t | 5 +- rt/t/api/template-insert.t | 2 +- rt/t/api/template-simple.t | 275 + rt/t/api/template.t | 5 +- rt/t/api/ticket.t | 53 +- rt/t/api/tickets.t | 29 +- rt/t/api/tickets_overlay_sql.t | 31 +- rt/t/api/txn_content.t | 2 +- rt/t/api/uri-fsck_com_rt.t | 7 +- rt/t/api/uri-t.t | 5 +- rt/t/api/user.t | 58 +- rt/t/api/users.t | 37 +- rt/t/api/versions_sorter.t | 22 + rt/t/api/web-config.t | 163 + rt/t/articles/article.t | 230 + rt/t/articles/articles.t | 137 + rt/t/articles/basic-api.t | 114 + rt/t/articles/cfsearch.t | 94 + rt/t/articles/class.t | 76 + rt/t/articles/interface.t | 221 + rt/t/articles/queue-specific-class.t | 218 + rt/t/articles/search-interface.t | 113 + rt/t/articles/upload-customfields.t | 90 + rt/t/articles/uri-a.t | 28 + rt/t/articles/uri-articles.t | 31 + rt/t/customfields/access_via_queue.t | 30 +- rt/t/customfields/api.t | 221 + rt/t/customfields/combo_cascade.t | 37 + rt/t/customfields/date_search.t | 119 + rt/t/customfields/datetime_search.t | 141 + rt/t/customfields/external.t | 56 + rt/t/customfields/ip.t | 285 + rt/t/customfields/iprange.t | 469 + rt/t/customfields/iprangev6.t | 474 + rt/t/customfields/ipv6.t | 252 + rt/t/customfields/pattern.t | 44 + rt/t/customfields/single_values.t | 37 + rt/t/customfields/sort_order.t | 20 +- rt/t/customfields/transaction.t | 60 + rt/t/data/configs/passwords | 2 + rt/t/fts/indexed_mysql.t | 134 + rt/t/fts/indexed_oracle.t | 82 + rt/t/fts/indexed_pg.t | 119 + rt/t/fts/not_indexed.t | 61 + rt/t/i18n/caching.t | 33 + rt/t/i18n/footer.t | 29 + rt/t/lifecycles/basics.t | 247 + rt/t/lifecycles/dates.t | 317 + rt/t/lifecycles/moving.t | 97 + rt/t/lifecycles/unresolved-deps.t | 45 + rt/t/lifecycles/utils.pl | 73 + rt/t/mail/bounce.t | 42 + rt/t/mail/charsets-outgoing.t | 67 +- rt/t/mail/crypt-gnupg.t | 114 +- rt/t/mail/dashboards.t | 397 + rt/t/mail/digest-attributes.t | 168 + rt/t/mail/disposition-outgoing.t | 69 + rt/t/mail/extractsubjecttag.t | 22 +- rt/t/mail/fake-sendmail | 27 + rt/t/mail/gateway.t | 201 +- rt/t/mail/gnupg-bad.t | 58 +- rt/t/mail/gnupg-incoming.t | 64 +- rt/t/mail/gnupg-outgoing-encrypted.t | 27 + rt/t/mail/gnupg-outgoing-plain.t | 25 + rt/t/mail/gnupg-outgoing-signed.t | 27 + rt/t/mail/gnupg-outgoing-signed_encrypted.t | 28 + rt/t/mail/gnupg-realmail.t | 36 +- rt/t/mail/gnupg-reverification.t | 77 +- rt/t/mail/gnupg-special.t | 33 +- rt/t/mail/mime_decoding.t | 36 +- rt/t/mail/multipart.t | 10 +- rt/t/mail/one-time-recipients.t | 209 + rt/t/mail/outlook.t | 15 +- rt/t/mail/rfc822-attachment.t | 137 + rt/t/mail/sendmail.t | 179 +- rt/t/mail/threading.t | 90 + rt/t/mail/verp.t | 2 +- rt/t/mail/wrong_mime_charset.t | 21 +- rt/t/ticket/clicky.t | 142 + rt/t/ticket/googleish_search.t | 43 + rt/t/web/admin_groups.t | 59 + rt/t/web/admin_user.t | 21 + rt/t/web/articles-links.t | 52 + rt/t/web/attachment-with-name-0.t | 23 + rt/t/web/attachment_encoding.t | 15 +- rt/t/web/attachments.t | 46 +- rt/t/web/basic.t | 35 +- rt/t/web/case-sensitivity.t | 85 + rt/t/web/cf_access.t | 129 +- rt/t/web/cf_date.t | 187 + rt/t/web/cf_datetime.t | 235 + rt/t/web/cf_onqueue.t | 24 +- rt/t/web/cf_render_type.t | 50 + rt/t/web/cf_select_one.t | 81 +- rt/t/web/charting.t | 25 +- rt/t/web/class_create.t | 75 + rt/t/web/clickjacking-preventions.t | 30 + rt/t/web/command_line.t | 65 +- rt/t/web/command_line_with_unknown_field.t | 11 +- rt/t/web/compilation_errors.t | 38 +- rt/t/web/config_tab_right.t | 8 +- rt/t/web/crypt-gnupg.t | 77 +- rt/t/web/custom_frontpage.t | 39 +- rt/t/web/custom_search.t | 6 +- rt/t/web/dashboards-basics.t | 268 + rt/t/web/dashboards-deleted-saved-search.t | 89 + rt/t/web/dashboards-groups.t | 139 +- rt/t/web/dashboards-permissions.t | 10 +- rt/t/web/dashboards-search-cache.t | 73 + rt/t/web/gnupg-headers.t | 53 + rt/t/web/gnupg-select-keys-on-create.t | 183 +- rt/t/web/gnupg-select-keys-on-update.t | 197 +- rt/t/web/gnupg-tickyboxes.t | 84 + rt/t/web/googleish_search.t | 219 + rt/t/web/group_create.t | 75 + .../Callbacks/logout.t/NoAuth/Logout.html/Default | 6 + rt/t/web/html/NoAuth/js/not-by-default.js | 3 + rt/t/web/html/delete-article-name-method.html | 15 + rt/t/web/html_template.t | 12 +- rt/t/web/logout.t | 39 + rt/t/web/offline.t | 78 + rt/t/web/offline_messages_utf8.t | 3 +- rt/t/web/offline_utf8.t | 15 +- rt/t/web/passthrough-jsmin | 5 + rt/t/web/path-traversal.t | 12 +- rt/t/web/private-components.t | 6 +- rt/t/web/query_builder.t | 100 +- rt/t/web/query_log.t | 20 + rt/t/web/queue_caching.t | 90 + rt/t/web/queue_create.t | 75 + rt/t/web/quickcreate.t | 37 + rt/t/web/quicksearch.t | 6 +- rt/t/web/redirect-after-login.t | 26 +- rt/t/web/redirect.t | 106 + rt/t/web/reminders.t | 88 + rt/t/web/remote_user.t | 36 + rt/t/web/requestor_groups_edit_link.t | 56 + rt/t/web/requestor_groups_limit.t | 36 + rt/t/web/rest-non-ascii-subject.t | 6 +- rt/t/web/rest-sort.t | 46 + rt/t/web/rest.t | 4 +- rt/t/web/richtext-autohandler.t | 10 +- rt/t/web/rights.t | 38 +- rt/t/web/rights1.t | 33 +- rt/t/web/saved_search_chart.t | 82 +- rt/t/web/saved_search_context.t | 69 + rt/t/web/saved_search_permissions.t | 6 +- rt/t/web/saved_search_update.t | 2 +- rt/t/web/scrips.t | 105 + rt/t/web/scrub.t | 46 + rt/t/web/search_bulk_update_links.t | 24 +- rt/t/web/search_cf_quotes.t | 53 + rt/t/web/search_rss.t | 11 +- rt/t/web/search_simple.t | 22 + rt/t/web/search_tabs.t | 86 + rt/t/web/self_service.t | 19 + rt/t/web/squish.t | 78 + rt/t/web/template.t | 62 + rt/t/web/ticket-create-utf8.t | 18 +- rt/t/web/ticket_display.t | 63 + rt/t/web/ticket_forward.t | 232 + rt/t/web/ticket_links.t | 110 + rt/t/web/ticket_modify_all.t | 44 + rt/t/web/ticket_modify_people.t | 113 + rt/t/web/ticket_owner.t | 147 +- rt/t/web/ticket_owner_autocomplete.t | 185 + rt/t/web/ticket_owner_issues_16656.t | 63 + rt/t/web/ticket_seen.t | 24 +- rt/t/web/ticket_txn_content.t | 71 +- rt/t/web/ticket_update_without_content.t | 14 +- rt/t/web/transaction_batch.t | 55 + rt/t/web/unlimited_search.t | 13 +- rt/t/web/user_update.t | 32 + rt/t/web/walk.t | 92 + 647 files changed, 401019 insertions(+), 1874 deletions(-) create mode 100755 rt/bin/rt create mode 100644 rt/devel/tools/apache.conf create mode 100644 rt/devel/tools/change-loc-msgstr create mode 100644 rt/devel/tools/extract-message-catalog create mode 100644 rt/devel/tools/factory create mode 100644 rt/devel/tools/license_tag create mode 100644 rt/devel/tools/merge-rosetta.pl create mode 100644 rt/devel/tools/rt-attributes-editor create mode 100644 rt/devel/tools/tweak-template-locstring create mode 100644 rt/docs/UPGRADING-2.0 create mode 100644 rt/docs/UPGRADING-3.0 create mode 100644 rt/docs/UPGRADING-3.2 create mode 100644 rt/docs/UPGRADING-3.4 create mode 100644 rt/docs/UPGRADING-3.6 create mode 100644 rt/docs/UPGRADING-3.8 create mode 100644 rt/docs/UPGRADING-4.0 create mode 100644 rt/docs/UPGRADING.mysql create mode 100644 rt/docs/customizing/articles_introduction.pod create mode 100644 rt/docs/customizing/templates.pod create mode 100644 rt/docs/customizing/timezones_in_charts.pod create mode 100644 rt/docs/extending/clickable_links.pod create mode 100644 rt/docs/extending/external_custom_fields.pod create mode 100644 rt/docs/extending/using_forms_widgets.pod create mode 100644 rt/docs/full_text_indexing.pod create mode 100644 rt/docs/glossary.pod create mode 100644 rt/docs/hacking.pod create mode 100644 rt/docs/network-diagram.svg create mode 100644 rt/docs/schema.dot create mode 100644 rt/docs/security.pod create mode 100644 rt/docs/web_deployment.pod create mode 100644 rt/etc/schema.mysql create mode 100644 rt/etc/upgrade/3.9.1/content create mode 100644 rt/etc/upgrade/3.9.2/content create mode 100644 rt/etc/upgrade/3.9.3/schema.Oracle create mode 100644 rt/etc/upgrade/3.9.3/schema.Pg create mode 100644 rt/etc/upgrade/3.9.3/schema.mysql create mode 100644 rt/etc/upgrade/3.9.5/backcompat create mode 100644 rt/etc/upgrade/3.9.5/schema.Oracle create mode 100644 rt/etc/upgrade/3.9.5/schema.Pg create mode 100644 rt/etc/upgrade/3.9.5/schema.SQLite create mode 100644 rt/etc/upgrade/3.9.5/schema.mysql create mode 100644 rt/etc/upgrade/3.9.6/schema.Oracle create mode 100644 rt/etc/upgrade/3.9.6/schema.Pg create mode 100644 rt/etc/upgrade/3.9.6/schema.SQLite create mode 100644 rt/etc/upgrade/3.9.6/schema.mysql create mode 100644 rt/etc/upgrade/3.9.7/content create mode 100644 rt/etc/upgrade/3.9.7/schema.Oracle create mode 100644 rt/etc/upgrade/3.9.7/schema.Pg create mode 100644 rt/etc/upgrade/3.9.7/schema.SQLite create mode 100644 rt/etc/upgrade/3.9.7/schema.mysql create mode 100644 rt/etc/upgrade/3.9.8/content create mode 100644 rt/etc/upgrade/3.9.8/schema.Oracle create mode 100644 rt/etc/upgrade/3.9.8/schema.Pg create mode 100644 rt/etc/upgrade/3.9.8/schema.SQLite create mode 100644 rt/etc/upgrade/3.9.8/schema.mysql create mode 100644 rt/etc/upgrade/4.0.0rc2/schema.mysql create mode 100644 rt/etc/upgrade/4.0.0rc4/schema.Oracle create mode 100644 rt/etc/upgrade/4.0.0rc4/schema.Pg create mode 100644 rt/etc/upgrade/4.0.0rc4/schema.mysql create mode 100644 rt/etc/upgrade/4.0.0rc7/content create mode 100644 rt/etc/upgrade/4.0.1/acl.Pg create mode 100644 rt/etc/upgrade/4.0.1/content create mode 100644 rt/etc/upgrade/4.0.3/content create mode 100644 rt/etc/upgrade/4.0.4/content create mode 100644 rt/etc/upgrade/sanity-check-stylesheets.pl create mode 100755 rt/etc/upgrade/upgrade-articles create mode 100644 rt/etc/upgrade/upgrade-articles.in create mode 100644 rt/lib/RT/Action/SetStatus.pm create mode 100644 rt/lib/RT/Article.pm create mode 100644 rt/lib/RT/Articles.pm create mode 100644 rt/lib/RT/Class.pm create mode 100644 rt/lib/RT/Classes.pm create mode 100644 rt/lib/RT/Dashboard/Mailer.pm create mode 100644 rt/lib/RT/Dashboards.pm create mode 100644 rt/lib/RT/Generated.pm create mode 100644 rt/lib/RT/Generated.pm.in create mode 100644 rt/lib/RT/Lifecycle.pm create mode 100644 rt/lib/RT/ObjectClass.pm create mode 100644 rt/lib/RT/ObjectClasses.pm create mode 100644 rt/lib/RT/ObjectTopic.pm create mode 100644 rt/lib/RT/ObjectTopics.pm create mode 100644 rt/lib/RT/SharedSettings.pm create mode 100644 rt/lib/RT/Squish.pm create mode 100644 rt/lib/RT/Squish/CSS.pm create mode 100644 rt/lib/RT/Squish/JS.pm create mode 100644 rt/lib/RT/Test/Apache.pm create mode 100644 rt/lib/RT/Test/GnuPG.pm create mode 100644 rt/lib/RT/Tickets_SQL.pm create mode 100644 rt/lib/RT/Topic.pm create mode 100644 rt/lib/RT/Topics.pm create mode 100644 rt/lib/RT/URI/a.pm create mode 100644 rt/lib/RT/URI/fsck_com_article.pm create mode 100755 rt/sbin/rt-dump-metadata create mode 100644 rt/sbin/rt-dump-metadata.in create mode 100755 rt/sbin/rt-fulltext-indexer create mode 100644 rt/sbin/rt-fulltext-indexer.in create mode 100755 rt/sbin/rt-preferences-viewer create mode 100644 rt/sbin/rt-preferences-viewer.in create mode 100755 rt/sbin/rt-server.fcgi create mode 100644 rt/sbin/rt-server.fcgi.in create mode 100755 rt/sbin/rt-setup-database create mode 100755 rt/sbin/rt-setup-fulltext-index create mode 100644 rt/sbin/rt-setup-fulltext-index.in create mode 100755 rt/sbin/rt-test-dependencies create mode 100755 rt/sbin/standalone_httpd create mode 100644 rt/sbin/standalone_httpd.in create mode 100644 rt/share/html/Admin/Articles/Classes/CustomFields.html create mode 100644 rt/share/html/Admin/Articles/Classes/GroupRights.html create mode 100644 rt/share/html/Admin/Articles/Classes/Modify.html create mode 100644 rt/share/html/Admin/Articles/Classes/Objects.html create mode 100644 rt/share/html/Admin/Articles/Classes/Topics.html create mode 100644 rt/share/html/Admin/Articles/Classes/UserRights.html create mode 100644 rt/share/html/Admin/Articles/Classes/index.html create mode 100644 rt/share/html/Admin/Articles/Elements/Topics create mode 100644 rt/share/html/Admin/Articles/index.html create mode 100644 rt/share/html/Admin/Elements/EditQueueWatcherGroup create mode 100644 rt/share/html/Admin/Elements/EditRights create mode 100644 rt/share/html/Admin/Elements/EditRightsCategoryTabs create mode 100644 rt/share/html/Admin/Elements/Portal create mode 100644 rt/share/html/Admin/Elements/SelectCustomFieldRenderType create mode 100644 rt/share/html/Admin/Global/CustomFields/Class-Article.html create mode 100644 rt/share/html/Admin/Global/Topics.html create mode 100644 rt/share/html/Admin/Tools/Queries.html create mode 100644 rt/share/html/Admin/Tools/Theme.html create mode 100644 rt/share/html/Articles/Article/Delete.html create mode 100644 rt/share/html/Articles/Article/Display.html create mode 100644 rt/share/html/Articles/Article/Edit.html create mode 100644 rt/share/html/Articles/Article/Elements/EditBasics create mode 100644 rt/share/html/Articles/Article/Elements/EditCustomFields create mode 100644 rt/share/html/Articles/Article/Elements/EditLinks create mode 100644 rt/share/html/Articles/Article/Elements/EditTopics create mode 100644 rt/share/html/Articles/Article/Elements/LinkEntryInstructions create mode 100644 rt/share/html/Articles/Article/Elements/Preformatted create mode 100644 rt/share/html/Articles/Article/Elements/SearchByCustomField create mode 100644 rt/share/html/Articles/Article/Elements/SelectSavedSearches create mode 100644 rt/share/html/Articles/Article/Elements/SelectSearchPrivacy create mode 100644 rt/share/html/Articles/Article/Elements/ShowHistory create mode 100644 rt/share/html/Articles/Article/Elements/ShowLinks create mode 100644 rt/share/html/Articles/Article/Elements/ShowSavedSearches create mode 100644 rt/share/html/Articles/Article/Elements/ShowSearchCriteria create mode 100644 rt/share/html/Articles/Article/Elements/ShowTopics create mode 100644 rt/share/html/Articles/Article/ExtractFromTicket.html create mode 100644 rt/share/html/Articles/Article/ExtractIntoClass.html create mode 100644 rt/share/html/Articles/Article/ExtractIntoTopic.html create mode 100644 rt/share/html/Articles/Article/History.html create mode 100644 rt/share/html/Articles/Article/PreCreate.html create mode 100644 rt/share/html/Articles/Article/Search.html create mode 100644 rt/share/html/Articles/Elements/BeforeMessageBox create mode 100644 rt/share/html/Articles/Elements/CheckSkipCreate create mode 100644 rt/share/html/Articles/Elements/CreateArticle create mode 100644 rt/share/html/Articles/Elements/GotoArticle create mode 100644 rt/share/html/Articles/Elements/IncludeArticle create mode 100644 rt/share/html/Articles/Elements/NewestArticles create mode 100644 rt/share/html/Articles/Elements/QuickSearch create mode 100644 rt/share/html/Articles/Elements/SelectClass create mode 100644 rt/share/html/Articles/Elements/ShowTopic create mode 100644 rt/share/html/Articles/Elements/UpdatedArticles create mode 100644 rt/share/html/Articles/Topics.html create mode 100644 rt/share/html/Articles/index.html create mode 100644 rt/share/html/Elements/EditCustomFieldDateTime create mode 100644 rt/share/html/Elements/EditCustomFieldIPAddress create mode 100644 rt/share/html/Elements/EditCustomFieldIPAddressRange create mode 100644 rt/share/html/Elements/Framekiller create mode 100644 rt/share/html/Elements/QueriesAsComment create mode 100644 rt/share/html/Elements/QueueSummaryByLifecycle create mode 100644 rt/share/html/Elements/QueueSummaryByStatus create mode 100644 rt/share/html/Elements/RT__Article/ColumnMap create mode 100644 rt/share/html/Elements/RT__Class/ColumnMap create mode 100644 rt/share/html/Elements/RT__Dashboard/ColumnMap create mode 100644 rt/share/html/Elements/SelectIPRelation create mode 100644 rt/share/html/Elements/SelectOwnerAutocomplete create mode 100644 rt/share/html/Elements/SelectOwnerDropdown create mode 100644 rt/share/html/Elements/ShowCustomFieldDateTime create mode 100644 rt/share/html/Elements/ShowReminders create mode 100644 rt/share/html/Elements/WidgetBar create mode 100644 rt/share/html/Helpers/Autocomplete/Groups create mode 100644 rt/share/html/Helpers/Autocomplete/Owners create mode 100644 rt/share/html/Helpers/Autocomplete/Users create mode 100644 rt/share/html/Helpers/TicketHistory create mode 100644 rt/share/html/Helpers/Toggle/ShowRequestor create mode 100644 rt/share/html/NoAuth/Helpers/CustomLogo/dhandler create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/LICENSE.html create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/adapters/jquery.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/ckeditor.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/config.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/contents.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/_languages.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/_translationstatus.txt create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/af.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/ar.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/bg.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/bn.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/bs.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/ca.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/cs.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/cy.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/da.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/de.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/el.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/en-au.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/en-ca.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/en-gb.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/en.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/eo.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/es.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/et.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/eu.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/fa.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/fi.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/fo.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/fr-ca.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/fr.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/gl.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/gu.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/he.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/hi.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/hr.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/hu.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/is.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/it.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/ja.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/km.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/ko.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/lt.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/lv.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/mn.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/ms.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/nb.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/nl.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/no.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/pl.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/pt-br.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/pt.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/ro.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/ru.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/sk.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/sl.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/sr-latn.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/sr.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/sv.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/th.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/tr.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/uk.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/vi.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/zh-cn.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/lang/zh.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/a11yhelp/dialogs/a11yhelp.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/a11yhelp/lang/en.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/a11yhelp/lang/he.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/about/dialogs/about.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/autogrow/plugin.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/clipboard/dialogs/paste.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/colordialog/dialogs/colordialog.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/dialog/dialogDefinition.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/div/dialogs/div.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/find/dialogs/find.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/flash/dialogs/flash.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/forms/dialogs/button.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/forms/dialogs/checkbox.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/forms/dialogs/form.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/forms/dialogs/hiddenfield.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/forms/dialogs/radio.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/forms/dialogs/select.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/forms/dialogs/textarea.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/forms/dialogs/textfield.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/iframedialog/plugin.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/image/dialogs/image.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/link/dialogs/anchor.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/link/dialogs/link.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/liststyle/dialogs/liststyle.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/pastefromword/filter/default.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/pastetext/dialogs/pastetext.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/scayt/dialogs/options.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/scayt/dialogs/toolbar.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/smiley/dialogs/smiley.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/specialchar/dialogs/specialchar.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/styles/styles/default.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/table/dialogs/table.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/tableresize/plugin.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/tabletools/dialogs/tableCell.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/templates/dialogs/templates.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/templates/templates/default.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/uicolor/dialogs/uicolor.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/uicolor/lang/en.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/uicolor/plugin.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/uicolor/yui/assets/yui.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/uicolor/yui/yui.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/wsc/dialogs/ciframe.html create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/wsc/dialogs/tmpFrameset.html create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/wsc/dialogs/wsc.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/plugins/wsc/dialogs/wsc.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/kama/dialog.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/kama/editor.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/kama/skin.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/kama/templates.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/office2003/dialog.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/office2003/editor.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/office2003/skin.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/office2003/templates.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/v2/dialog.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/v2/editor.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/v2/skin.js create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/skins/v2/templates.css create mode 100644 rt/share/html/NoAuth/RichText/ckeditor/themes/default/theme.js create mode 100644 rt/share/html/NoAuth/css/aileron/InHeader create mode 100644 rt/share/html/NoAuth/css/aileron/base.css create mode 100644 rt/share/html/NoAuth/css/aileron/boxes.css create mode 100644 rt/share/html/NoAuth/css/aileron/forms.css create mode 100644 rt/share/html/NoAuth/css/aileron/images/dhandler create mode 100644 rt/share/html/NoAuth/css/aileron/layout.css create mode 100644 rt/share/html/NoAuth/css/aileron/login.css create mode 100644 rt/share/html/NoAuth/css/aileron/main.css create mode 100644 rt/share/html/NoAuth/css/aileron/misc.css create mode 100644 rt/share/html/NoAuth/css/aileron/msie-pie.css create mode 100644 rt/share/html/NoAuth/css/aileron/msie.css create mode 100644 rt/share/html/NoAuth/css/aileron/msie6.css create mode 100644 rt/share/html/NoAuth/css/aileron/nav.css create mode 100644 rt/share/html/NoAuth/css/aileron/ticket-lists.css create mode 100644 rt/share/html/NoAuth/css/aileron/ticket-search.css create mode 100644 rt/share/html/NoAuth/css/aileron/ticket.css create mode 100644 rt/share/html/NoAuth/css/ballard/InHeader create mode 100644 rt/share/html/NoAuth/css/ballard/base.css create mode 100644 rt/share/html/NoAuth/css/ballard/boxes.css create mode 100644 rt/share/html/NoAuth/css/ballard/images/dhandler create mode 100644 rt/share/html/NoAuth/css/ballard/layout.css create mode 100644 rt/share/html/NoAuth/css/ballard/main.css create mode 100644 rt/share/html/NoAuth/css/ballard/misc.css create mode 100644 rt/share/html/NoAuth/css/ballard/msie.css create mode 100644 rt/share/html/NoAuth/css/ballard/msie6.css create mode 100644 rt/share/html/NoAuth/css/ballard/nav.css create mode 100644 rt/share/html/NoAuth/css/ballard/ticket-lists.css create mode 100644 rt/share/html/NoAuth/css/ballard/ticket-search.css create mode 100644 rt/share/html/NoAuth/css/ballard/ticket.css create mode 100644 rt/share/html/NoAuth/css/base/admin.css create mode 100644 rt/share/html/NoAuth/css/base/articles.css create mode 100644 rt/share/html/NoAuth/css/base/collection.css create mode 100644 rt/share/html/NoAuth/css/base/farbtastic.css create mode 100644 rt/share/html/NoAuth/css/base/forms.css create mode 100644 rt/share/html/NoAuth/css/base/history-folding.css create mode 100644 rt/share/html/NoAuth/css/base/jquery-ui.css create mode 100644 rt/share/html/NoAuth/css/base/jquery-ui.custom.modified.css create mode 100644 rt/share/html/NoAuth/css/base/login.css create mode 100644 rt/share/html/NoAuth/css/base/main.css create mode 100644 rt/share/html/NoAuth/css/base/nav.css create mode 100644 rt/share/html/NoAuth/css/base/portlets.css create mode 100644 rt/share/html/NoAuth/css/base/rights-editor.css create mode 100644 rt/share/html/NoAuth/css/base/superfish-navbar.css create mode 100644 rt/share/html/NoAuth/css/base/superfish-vertical.css create mode 100644 rt/share/html/NoAuth/css/base/superfish.css create mode 100644 rt/share/html/NoAuth/css/base/tablesorter.css create mode 100644 rt/share/html/NoAuth/css/base/theme-editor.css create mode 100644 rt/share/html/NoAuth/css/base/ticket-form.css create mode 100644 rt/share/html/NoAuth/css/base/ticket.css create mode 100644 rt/share/html/NoAuth/css/base/tools.css create mode 100644 rt/share/html/NoAuth/css/base/ui.timepickr.css create mode 100644 rt/share/html/NoAuth/css/base/ui.timepickr.custom.css create mode 100644 rt/share/html/NoAuth/css/base/yui-fonts.css create mode 100644 rt/share/html/NoAuth/css/images/PIE.htc create mode 100644 rt/share/html/NoAuth/css/web2/msie-pie.css create mode 100644 rt/share/html/NoAuth/js/dhandler create mode 100644 rt/share/html/NoAuth/js/farbtastic.js create mode 100644 rt/share/html/NoAuth/js/history-folding.js create mode 100644 rt/share/html/NoAuth/js/jquery-1.4.2.min.js create mode 100644 rt/share/html/NoAuth/js/jquery-ui-1.8.4.custom.min.js create mode 100644 rt/share/html/NoAuth/js/jquery-ui-patch-datepicker.js create mode 100644 rt/share/html/NoAuth/js/jquery.event.hover-1.0.js create mode 100644 rt/share/html/NoAuth/js/jquery.supposition.js create mode 100644 rt/share/html/NoAuth/js/jquery.tablesorter.min.js create mode 100644 rt/share/html/NoAuth/js/jquery_noconflict.js create mode 100644 rt/share/html/NoAuth/js/late.js create mode 100644 rt/share/html/NoAuth/js/superfish.js create mode 100644 rt/share/html/NoAuth/js/supersubs.js create mode 100644 rt/share/html/NoAuth/js/ui.timepickr.js create mode 100644 rt/share/html/NoAuth/js/userautocomplete.js create mode 100644 rt/share/html/Search/Article.html create mode 100644 rt/share/html/Search/Elements/Article create mode 100644 rt/share/html/Search/Elements/EditSort create mode 100644 rt/share/html/SelfService/Article/Display.html create mode 100644 rt/share/html/SelfService/Article/Search.html create mode 100644 rt/share/html/SelfService/Article/autohandler create mode 100644 rt/share/html/SelfService/Elements/SearchArticle create mode 100644 rt/share/html/Ticket/Elements/AddAttachments create mode 100644 rt/share/html/Ticket/Elements/ClickToShowHistory create mode 100644 rt/share/html/Ticket/Elements/FindTransactions create mode 100644 rt/share/html/Ticket/Elements/FoldStanzaJS create mode 100644 rt/share/html/Ticket/Elements/ShowRequestorExtraInfo create mode 100644 rt/share/html/Ticket/Elements/ShowRequestorTickets create mode 100644 rt/share/html/Ticket/Elements/ShowRequestorTicketsActive create mode 100644 rt/share/html/Ticket/Elements/ShowRequestorTicketsAll create mode 100644 rt/share/html/Ticket/Elements/ShowRequestorTicketsInactive create mode 100644 rt/share/html/Ticket/Elements/ShowSimplifiedRecipients create mode 100644 rt/share/html/Ticket/autohandler create mode 100644 rt/share/html/Tools/MyReminders.html create mode 100644 rt/share/html/m/ticket/autohandler create mode 100644 rt/share/po/bg.po create mode 100644 rt/share/po/cs.po create mode 100644 rt/share/po/da.po create mode 100644 rt/share/po/de.po create mode 100644 rt/share/po/el.po create mode 100644 rt/share/po/en.po create mode 100644 rt/share/po/en_GB.po create mode 100644 rt/share/po/es.po create mode 100644 rt/share/po/et.po create mode 100644 rt/share/po/fi.po create mode 100644 rt/share/po/fr.po create mode 100644 rt/share/po/he.po create mode 100644 rt/share/po/hr.po create mode 100644 rt/share/po/hu.po create mode 100644 rt/share/po/id.po create mode 100644 rt/share/po/is.po create mode 100644 rt/share/po/it.po create mode 100644 rt/share/po/ja.po create mode 100644 rt/share/po/lt.po create mode 100644 rt/share/po/lv.po create mode 100644 rt/share/po/mk.po create mode 100644 rt/share/po/nb.po create mode 100644 rt/share/po/nl.po create mode 100644 rt/share/po/nn.po create mode 100644 rt/share/po/pl.po create mode 100644 rt/share/po/pt.po create mode 100644 rt/share/po/pt_BR.po create mode 100644 rt/share/po/pt_PT.po create mode 100644 rt/share/po/rt.pot create mode 100644 rt/share/po/ru.po create mode 100644 rt/share/po/sl.po create mode 100644 rt/share/po/sv.po create mode 100644 rt/share/po/tr.po create mode 100644 rt/share/po/zh_CN.po create mode 100644 rt/share/po/zh_TW.po create mode 100644 rt/t/api/bookmarks.t create mode 100644 rt/t/api/canonical_charset.t create mode 100644 rt/t/api/cf_render_type.t create mode 100644 rt/t/api/config.t create mode 100644 rt/t/api/cron.t create mode 100644 rt/t/api/execute-code.t create mode 100644 rt/t/api/group-rights.t create mode 100644 rt/t/api/has_rights.t create mode 100644 rt/t/api/i18n_guess.t create mode 100644 rt/t/api/rtname.t create mode 100644 rt/t/api/safe-run-child-util.t create mode 100644 rt/t/api/savedsearch.t create mode 100644 rt/t/api/squish.t create mode 100644 rt/t/api/template-simple.t create mode 100644 rt/t/api/versions_sorter.t create mode 100644 rt/t/api/web-config.t create mode 100644 rt/t/articles/article.t create mode 100644 rt/t/articles/articles.t create mode 100644 rt/t/articles/basic-api.t create mode 100644 rt/t/articles/cfsearch.t create mode 100644 rt/t/articles/class.t create mode 100644 rt/t/articles/interface.t create mode 100644 rt/t/articles/queue-specific-class.t create mode 100644 rt/t/articles/search-interface.t create mode 100644 rt/t/articles/upload-customfields.t create mode 100644 rt/t/articles/uri-a.t create mode 100644 rt/t/articles/uri-articles.t create mode 100644 rt/t/customfields/api.t create mode 100644 rt/t/customfields/combo_cascade.t create mode 100644 rt/t/customfields/date_search.t create mode 100644 rt/t/customfields/datetime_search.t create mode 100644 rt/t/customfields/external.t create mode 100644 rt/t/customfields/ip.t create mode 100644 rt/t/customfields/iprange.t create mode 100644 rt/t/customfields/iprangev6.t create mode 100644 rt/t/customfields/ipv6.t create mode 100644 rt/t/customfields/pattern.t create mode 100644 rt/t/customfields/single_values.t create mode 100644 rt/t/customfields/transaction.t create mode 100644 rt/t/data/configs/passwords create mode 100644 rt/t/fts/indexed_mysql.t create mode 100644 rt/t/fts/indexed_oracle.t create mode 100644 rt/t/fts/indexed_pg.t create mode 100644 rt/t/fts/not_indexed.t create mode 100644 rt/t/i18n/caching.t create mode 100644 rt/t/i18n/footer.t create mode 100644 rt/t/lifecycles/basics.t create mode 100644 rt/t/lifecycles/dates.t create mode 100644 rt/t/lifecycles/moving.t create mode 100644 rt/t/lifecycles/unresolved-deps.t create mode 100644 rt/t/lifecycles/utils.pl create mode 100644 rt/t/mail/bounce.t create mode 100644 rt/t/mail/dashboards.t create mode 100644 rt/t/mail/digest-attributes.t create mode 100644 rt/t/mail/disposition-outgoing.t create mode 100644 rt/t/mail/fake-sendmail create mode 100644 rt/t/mail/gnupg-outgoing-encrypted.t create mode 100644 rt/t/mail/gnupg-outgoing-plain.t create mode 100644 rt/t/mail/gnupg-outgoing-signed.t create mode 100644 rt/t/mail/gnupg-outgoing-signed_encrypted.t create mode 100644 rt/t/mail/one-time-recipients.t create mode 100644 rt/t/mail/rfc822-attachment.t create mode 100644 rt/t/mail/threading.t create mode 100644 rt/t/ticket/clicky.t create mode 100644 rt/t/ticket/googleish_search.t create mode 100644 rt/t/web/admin_groups.t create mode 100644 rt/t/web/admin_user.t create mode 100644 rt/t/web/articles-links.t create mode 100644 rt/t/web/attachment-with-name-0.t create mode 100644 rt/t/web/case-sensitivity.t create mode 100644 rt/t/web/cf_date.t create mode 100644 rt/t/web/cf_datetime.t create mode 100644 rt/t/web/cf_render_type.t create mode 100644 rt/t/web/class_create.t create mode 100644 rt/t/web/clickjacking-preventions.t create mode 100644 rt/t/web/dashboards-basics.t create mode 100644 rt/t/web/dashboards-deleted-saved-search.t create mode 100644 rt/t/web/dashboards-search-cache.t create mode 100644 rt/t/web/gnupg-headers.t create mode 100644 rt/t/web/gnupg-tickyboxes.t create mode 100644 rt/t/web/googleish_search.t create mode 100644 rt/t/web/group_create.t create mode 100644 rt/t/web/html/Callbacks/logout.t/NoAuth/Logout.html/Default create mode 100644 rt/t/web/html/NoAuth/js/not-by-default.js create mode 100644 rt/t/web/html/delete-article-name-method.html create mode 100644 rt/t/web/logout.t create mode 100644 rt/t/web/offline.t create mode 100644 rt/t/web/passthrough-jsmin create mode 100644 rt/t/web/query_log.t create mode 100644 rt/t/web/queue_caching.t create mode 100644 rt/t/web/queue_create.t create mode 100644 rt/t/web/quickcreate.t create mode 100644 rt/t/web/redirect.t create mode 100644 rt/t/web/reminders.t create mode 100644 rt/t/web/remote_user.t create mode 100644 rt/t/web/requestor_groups_edit_link.t create mode 100644 rt/t/web/requestor_groups_limit.t create mode 100644 rt/t/web/rest-sort.t create mode 100644 rt/t/web/saved_search_context.t create mode 100644 rt/t/web/scrips.t create mode 100644 rt/t/web/scrub.t create mode 100644 rt/t/web/search_cf_quotes.t create mode 100644 rt/t/web/search_simple.t create mode 100644 rt/t/web/search_tabs.t create mode 100644 rt/t/web/self_service.t create mode 100644 rt/t/web/squish.t create mode 100644 rt/t/web/template.t create mode 100644 rt/t/web/ticket_display.t create mode 100644 rt/t/web/ticket_forward.t create mode 100644 rt/t/web/ticket_links.t create mode 100644 rt/t/web/ticket_modify_all.t create mode 100644 rt/t/web/ticket_modify_people.t create mode 100644 rt/t/web/ticket_owner_autocomplete.t create mode 100644 rt/t/web/ticket_owner_issues_16656.t create mode 100644 rt/t/web/transaction_batch.t create mode 100644 rt/t/web/user_update.t create mode 100644 rt/t/web/walk.t diff --git a/rt/bin/rt b/rt/bin/rt new file mode 100755 index 000000000..01e4a190e --- /dev/null +++ b/rt/bin/rt @@ -0,0 +1,2616 @@ +#!/usr/bin/perl -w +# BEGIN BPS TAGGED BLOCK {{{ +# +# COPYRIGHT: +# +# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC +# +# +# (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 + +use strict; + +if ( $ARGV[0] && $ARGV[0] =~ /^(?:--help|-h)$/ ) { + require Pod::Usage; + print Pod::Usage::pod2usage( { verbose => 2 } ); + exit; +} + +# 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 = Session->new("$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 [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 = Term::ReadLine->new('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() }) { + 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 = ); + 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 = ); + 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 = ; + } + 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 () { $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 = LWP::UserAgent->new(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}; + + open( my $handle, '<', $file ) or return 0; + + $self->{file} = $file; + my $sids = $self->{sids} = {}; + while (<$handle>) { + chomp; + next if /^$/ || /^#/; + next unless m#^https?://[^ ]+ \w+ [^;,\s]+=[0-9A-Fa-f]+$#; + my ($server, $user, $cookie) = split / /, $_; + $sids->{$server}{$user} = $cookie; + } + return 1; + } + + # Writes the current session cache to the specified file. + sub save { + my ($self, $file) = shift; + $file ||= $self->{file}; + + open( my $handle, '>', "$file" ) or return 0; + + my $sids = $self->{sids}; + foreach my $server (keys %$sids) { + foreach my $user (keys %{ $sids->{$server} }) { + my $sid = $sids->{$server}{$user}; + if (defined $sid) { + print $handle "$server $user $sid\n"; + } + } + } + close($handle); + chmod 0600, $file; + return 1; + } + + 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", "/opt/rt3/local/etc/rt.conf", "/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( my $handle, '<', $file ) or return; + + while (<$handle>) { + 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 $/ = undef; + + open( my $handle, '>', $file ) or die "$file: $!\n"; + print $handle $text; + close($handle); + + system($editor, $file) && die "Couldn't run $editor.\n"; + + open( $handle, '<', $file ) or die "$file: $!\n"; + $text = <$handle>; + close($handle); + + 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 /\s*,\s*/, $line, 2; + + while ($a) { + no warnings 'uninitialized'; + if ( $a =~ /^'/ ) { + my $s = $a; + while ( $a !~ /'$/ || ( $a !~ /(\\\\)+'$/ + && $a =~ /(\\)+'$/ )) { + ( $a, $b ) = split /\s*,\s*/, $b, 2; + $s .= ',' . $a; + } + push @words, $s; + } + elsif ( $a =~ /^q{/ ) { + my $s = $a; + while ( $a !~ /}$/ ) { + ( $a, $b ) = + split /\s*,\s*/, $b, 2; + $s .= ',' . $a; + } + $s =~ s/^q{/'/; + $s =~ s/}/'/; + push @words, $s; + } + else { + push @words, $a; + } + ( $a, $b ) = split /\s*,\s*/, $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 /\s*,\s*/, $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 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 [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 ". 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 " [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, 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, /opt/rt3/local/etc/rt.conf + and /etc/rt.conf. + + Configuration directives: + + The following directives may occur, one per line: + + - server URL to RT server. + - user RT username. + - passwd RT user's password. + - query Default RT Query for list action + - orderby Default RT order for list action + - queue 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: + + /[/] + + 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/ + + 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-specific details) + - rt help (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-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-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/ Only a particular type of transaction. + history/id/ Only the transaction of the specified id. + attachments A list of attachments. + attachments/ The metadata for an individual attachment. + attachments//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 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 + + Change the subject of a ticket whose ticket id is given. + +-- + +Title: give +Text: + + Syntax: + + rt give + + Give a ticket whose ticket id is given to another user. + +-- + +Title: steal +Text: + + rt steal + + Steal a ticket whose ticket id is given, i.e. set the owner to myself. + +-- + +Title: take +Text: + + Syntax: + + rt take + + Take a ticket whose ticket id is given, i.e. set the owner to myself. + +-- + +Title: untake +Text: + + Syntax: + + rt untake + + Untake a ticket whose ticket id is given, i.e. set the owner to Nobody. + +-- + +Title: resolve +Title: res +Text: + + Syntax: + + rt resolve + + Resolves a ticket whose ticket id is given. + +-- + +Title: delete +Title: del +Text: + + Syntax: + + rt delete + + 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 [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 + RT. (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 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] + + 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 " + 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] 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 " 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 [options] + + 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 Specify comment text. + -a Attach a file to the comment. (May be used more + than once to attach multiple files.) + -c A comma-separated list of Cc addresses. + -b A comma-separated list of Bcc addresses. + -w